当前位置: 首页 > news >正文

Harmony鸿蒙开发0基础入门到精通Day10--JavaScript篇

单例模式

单例模式是设计模式中创建型模式的一种,核心目标是保证一个类(或对象)在整个应用中只有一个实例,并提供一个全局统一的访问点,避免重复创建实例造成资源浪费或数据不一致。

单例模式的实现依赖两个关键逻辑:

  1. 控制实例创建:通过私有化构造函数、判断实例是否已存在等方式,阻止外部重复创建实例;
  2. 提供全局访问:暴露一个静态方法或全局对象,让外部只能通过这个 “入口” 获取唯一实例。

利用闭包 “持久化保存实例状态” 的特性,将实例隐藏在闭包中,仅暴露获取实例的方法,防止外部直接修改。

// 闭包实现单例:创建一个唯一的弹窗实例
const ModalSingleton = (function() {let instance = null; // 闭包中保存唯一实例// 私有方法:创建弹窗 DOM(模拟实例初始化逻辑)function createModal() {const modal = document.createElement('div');modal.style.display = 'none';modal.textContent = '全局唯一弹窗';document.body.appendChild(modal);return modal;}// 暴露的全局访问点:获取实例(不存在则创建)return {getInstance: function() {if (!instance) { // 关键:判断实例是否已存在instance = createModal();}return instance;}};
})();// 使用:多次调用 getInstance,获取的是同一个实例
const modal1 = ModalSingleton.getInstance();
const modal2 = ModalSingleton.getInstance();
console.log(modal1 === modal2); // true(实例唯一)// 显示弹窗(操作同一个 DOM 元素)
modal1.style.display = 'block';

通过 ES6 类的静态方法(static)控制实例创建,同时在构造函数中判断是否已有实例,防止外部通过 new 关键字重复创建。

class LoggerSingleton {// 静态属性:保存唯一实例(用 # 私有化,防止外部修改)static #instance = null;// 私有方法:初始化日志配置(模拟实例逻辑)#initConfig() {this.logLevel = 'info';this.logDir = './logs';}// 构造函数:私有化(ES6 用 # 标识私有构造函数,禁止外部 new)constructor() {if (LoggerSingleton.#instance) {throw new Error('Logger 已存在实例,请通过 getInstance 获取');}this.#initConfig();}// 静态方法:全局访问点,获取唯一实例static getInstance() {if (!LoggerSingleton.#instance) {LoggerSingleton.#instance = new LoggerSingleton();}return LoggerSingleton.#instance;}// 实例方法:日志打印(业务逻辑)log(message) {console.log(`[${this.logLevel}] ${message}`);}
}// 使用:通过静态方法获取实例
const logger1 = LoggerSingleton.getInstance();
const logger2 = LoggerSingleton.getInstance();
console.log(logger1 === logger2); // true// 禁止外部 new(会报错)
// const logger3 = new LoggerSingleton(); // 报错:Constructor of class 'LoggerSingleton' is privatelogger1.log('应用启动'); // [info] 应用启动
// 其他文件1:a.js
import logger from './logger.js';
logger.log('a.js 日志'); // [info] a.js 日志// 其他文件2:b.js
import logger from './logger.js';
logger.log('b.js 日志'); // [info] b.js 日志// 验证:a.js 和 b.js 导入的是同一个实例
// 在 a.js 中修改 logger.logLevel = 'error',b.js 中打印会变成 error 级别

策略模式

策略模式(Strategy Pattern)是行为型设计模式的一种,核心思想是:将一组可替换的算法(或行为)封装成独立的 “策略”,让它们可以在运行时动态切换,而不影响使用策略的客户端。其本质是 “分离算法的定义与使用”,避免用大量 if-else 或 switch 语句判断逻辑,提高代码的灵活性和可维护性。

为什么需要策略模式?(解决的问题)

没有策略模式时,若存在多种可选算法,通常会用 if-else 堆叠判断,导致代码臃肿、难以扩展:

// 反例:不用策略模式,用 if-else 处理多种支付方式
function calculatePrice(price, payType) {if (payType === 'alipay') {return price * 0.9; // 支付宝9折} else if (payType === 'wechat') {return price * 0.85; // 微信8.5折} else if (payType === 'card') {return price * 0.8; // 银行卡8折} else {throw new Error('未知支付方式');}
}// 使用时
console.log(calculatePrice(100, 'alipay')); // 90

问题

  • 新增支付方式(如 cash 现金)需修改 calculatePrice 函数,违反 “开闭原则”(对扩展开放,对修改关闭);
  • 逻辑复杂时,if-else 堆叠导致代码可读性差、维护困难。

JavaScript 中函数是 “一等公民”,策略模式可简化实现(无需严格的类结构),核心是用对象封装策略,用环境函数调度策略

// 1. 定义策略:封装不同支付方式的折扣算法(统一接口:接收price,返回计算后价格)
const payStrategies = {alipay: (price) => price * 0.9,wechat: (price) => price * 0.85,card: (price) => price * 0.8,cash: (price) => price // 新增现金支付(无折扣),无需修改其他代码
};// 2. 定义环境:负责接收请求,委托给对应的策略
function calculatePrice(price, payType) {// 检查策略是否存在if (!payStrategies[payType]) {throw new Error('未知支付方式');}// 调用对应策略计算价格return payStrategies[payType](price);
}// 使用:直接指定策略类型
console.log(calculatePrice(100, 'alipay')); // 90
console.log(calculatePrice(200, 'cash')); // 200(新增策略直接可用)

改进点

  • 新增支付方式只需在 payStrategies 中添加键值对,无需修改 calculatePrice 函数,符合 “开闭原则”;
  • 策略逻辑与调度逻辑分离,代码更清晰。

观察者模式

观察者模式(Observer Pattern)是行为型设计模式的一种,核心思想是:建立对象间的 “一对多” 依赖关系,当一个对象(称为 “主题 / 被观察者”)的状态发生变化时,所有依赖它的对象(称为 “观察者”)会自动收到通知并更新,实现 “状态变化 -> 自动同步” 的效果。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body></body>
<script>/*** 学生老师* 观察者模式* * 观察者 老师   一个函数* * 被观察者 学生  一个函数* * 一旦被观察者发生动态改变  那么要及时通知观察者 观察者会采取对应的措施* ***///  自定义函数  观察者class Observer {constructor(name, fn = () => { }) {this.name = namethis.fn = fn}}// 创建两个观察者const p1 = new Observer('班主任', (state) => {console.log(`班主任看你在:${state}`);})const p2 = new Observer('咨询师', (state) => {console.log(`咨询师看你在:${state}`);})// 被观察者class Subject {constructor(state) {// 自己定义自己的状态this.state = state// 是不是可以定义一个变量用来存储谁偷看我呢this.observer = []}// 定义一个方法需要知道刚才谁看我了addObserver(obs) {// 如果之前她已经来过了 就不需要再次添加// 如果是第一次来 那是不是我就应该要找个变量存储一下this.observer = this.observer.filter(item => item != obs)// 上面判断的是 当前observer中是没有这个变量,this.observer.push(obs)}}// 当前需要实例化被观察者const s1 = new Subject('听课')s1.addObserver(p1)s1.addObserver(p2)s1.addObserver(p1)s1.addObserver(p2)console.log(s1);</script></html>

发布订阅模式

发布订阅模式(Publish-Subscribe Pattern)是行为型设计模式的一种,核心思想是:通过一个 “事件总线(Event Bus)” 中间层,实现发布者(Publisher)和订阅者(Subscriber)的完全解耦。发布者无需知道订阅者的存在,订阅者也无需知道发布者的信息,两者通过 “事件” 间接通信 —— 发布者发布事件到总线,总线将事件通知给所有订阅了该事件的订阅者。

核心角色与工作流程

发布订阅模式比观察者模式多了一层 “事件总线”,核心角色和流程如下:

1. 核心角色
  • 发布者(Publisher):负责发布事件到事件总线,不直接与订阅者交互;
  • 订阅者(Subscriber):通过事件总线订阅感兴趣的事件,当事件被发布时接收通知并处理;
  • 事件总线(Event Bus):中间层,管理所有事件的订阅关系(存储 “事件类型→订阅者回调” 的映射),并在事件发布时通知对应订阅者。
2. 工作流程
  1. 订阅:订阅者通过事件总线的 on 方法,订阅特定事件(如 'message'),并注册回调函数(事件触发时执行的逻辑);
  2. 发布:发布者通过事件总线的 emit 方法,发布特定事件,并传递事件数据;
  3. 通知:事件总线收到事件后,遍历该事件的所有订阅者回调,依次执行(可传递事件数据);
  4. 取消订阅:订阅者可通过 off 方法取消对某个事件的订阅,不再接收通知。

与观察者模式的核心区别(关键!)

发布订阅模式常与观察者模式混淆,但两者的解耦程度结构有本质区别:

对比维度观察者模式发布订阅模式
核心结构主题(Subject)直接管理观察者(无中间层)发布者、订阅者通过 “事件总线” 间接交互(有中间层)
耦合度主题与观察者轻度耦合(主题知道观察者的 update 方法)发布者与订阅者完全解耦(彼此不知道对方存在)
通信方式主题主动调用观察者的方法发布者发布事件到总线,总线转发给订阅者
适用场景单一主题与多个观察者的直接关联(如 DOM 事件)跨模块 / 跨系统的松散耦合通信(如全局事件、消息队列)

形象比喻

  • 观察者模式:老师(主题)直接点名通知学生(观察者)交作业;
  • 发布订阅模式:老师(发布者)在班级群(事件总线)发 “交作业” 通知,所有订阅了群消息的学生(订阅者)收到通知。

JavaScript 中的发布订阅模式实现(事件总线)

在 JavaScript 中,发布订阅模式的核心是实现一个 “事件总线”,提供 on(订阅)、emit(发布)、off(取消订阅)、once(一次性订阅)等方法。

class EventBus {constructor() {// 存储事件映射:{ 事件类型: [回调1, 回调2, ...] }this.events = Object.create(null); }/*** 订阅事件* @param {string} type - 事件类型(如 'message')* @param {Function} callback - 事件触发时的回调函数*/on(type, callback) {// 初始化事件对应的回调数组(首次订阅时)if (!this.events[type]) {this.events[type] = [];}// 添加回调(去重:避免重复订阅同一回调)if (!this.events[type].includes(callback)) {this.events[type].push(callback);}}/*** 发布事件* @param {string} type - 事件类型* @param  {...any} args - 传递给订阅者的参数(可多个)*/emit(type, ...args) {// 若事件无订阅者,直接返回if (!this.events[type]) return;// 遍历所有订阅者回调,执行并传递参数this.events[type].forEach(callback => {callback.apply(this, args); // 绑定 this 为 EventBus 实例});}/*** 取消订阅* @param {string} type - 事件类型* @param {Function} callback - 要取消的回调函数(不传则取消该事件所有订阅)*/off(type, callback) {if (!this.events[type]) return;// 若未传 callback,清空该事件所有订阅if (!callback) {this.events[type] = [];return;}// 否则,移除指定回调this.events[type] = this.events[type].filter(cb => cb !== callback);}/*** 一次性订阅(触发一次后自动取消)* @param {string} type - 事件类型* @param {Function} callback - 回调函数*/once(type, callback) {// 包装回调:执行后自动取消订阅const wrapper = (...args) => {callback.apply(this, args); // 执行原回调this.off(type, wrapper); // 取消自身订阅};this.on(type, wrapper);}
}

深浅拷贝

深浅拷贝是 JavaScript 中处理引用类型数据(如对象、数组)复制的核心概念,核心区别在于是否递归复制引用类型的内部层级:浅拷贝仅复制表层属性,引用类型仍共享原数据;深拷贝则完全复制所有层级,生成独立的新对象,两者的选择直接影响数据独立性和代码安全性。

核心概念:先明确基本类型与引用类型

深浅拷贝的差异仅针对引用类型,需先区分两种数据类型的存储机制:

  • 基本类型(number、string、boolean、null、undefined、Symbol、BigInt):值直接存储在栈内存,拷贝时直接复制值,不存在深浅之分;
  • 引用类型(object、array、function、Map、Set 等):值存储在堆内存,变量仅保存堆内存的 “地址引用”,拷贝时若只复制地址则为浅拷贝,复制地址指向的所有数据则为深拷贝。

浅拷贝

只复制对象的表层属性

  • 基本类型属性:直接复制值,新对象与原对象的基本类型属性独立;
  • 引用类型属性:仅复制 “地址引用”,新对象与原对象的引用类型属性共享同一堆内存数据,修改一方会影响另一方。
 常见实现方法(附示例)
实现方式适用场景示例代码
Object.assign()对象表层拷贝const obj2 = Object.assign({}, obj1);
扩展运算符(...)对象 / 数组表层拷贝const obj2 = { ...obj1 };const arr2 = [ ...arr1 ];
数组 slice ()数组表层拷贝(无参数)const arr2 = arr1.slice();(slice (0) 效果相同)
数组 concat ()数组表层拷贝(空参数)const arr2 = arr1.concat();
Array.from()数组表层拷贝const arr2 = Array.from(arr1);
浅拷贝的特点与问题
  • 优点:性能高(仅复制表层),实现简单;
  • 问题:引用类型属性共享,修改会相互影响(典型坑点)。

深拷贝(Deep Copy):完全复制,独立无关联

原理

递归复制对象的所有层级属性

  • 基本类型属性:复制值;
  • 引用类型属性:递归遍历其内部所有层级,生成新的独立对象(新的堆内存地址),新对象与原对象完全独立,修改一方不影响另一方。
const obj1 = { name: "Alice", address: { city: "Beijing" } };
const obj2 = JSON.parse(JSON.stringify(obj1));// 修改引用类型属性:互不影响
obj2.address.city = "Shanghai";
console.log(obj1.address.city); // "Beijing"(原对象不变)

局限性(必知!)

  • 无法拷贝 函数、undefined、Symbol(会被忽略);
  • 无法拷贝 循环引用(如 obj1.self = obj1,会报错);
  • 无法拷贝 特殊对象(如 Map、Set、Date、RegExp,会被转为普通对象或丢失信息)。
  • 自定义递归函数(灵活可控)

    原理:通过递归遍历对象 / 数组的每一层,判断属性类型,基本类型直接复制,引用类型则创建新对象 / 数组后继续递归。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body></body>
<script>let obj = {name: '张三',age: 18,infor: {id: 10,gender: '男'}}// //浅拷贝// let obj2 = {...obj}// obj.infor.id = 100// console.log(obj2);//infor:{id: 100, gender: '男'}//这里属于浅拷贝,对于引用数据来说,是一个地址//这样的话,修改地址里的值,拷贝过来的地址所对应的值也会发生改变。//这是浅拷贝的弊端。//target是新对象 obj是旧对象function cloneDFS(target, obj) {for (let k in obj) {// if (Object.prototype.toString.call(obj[k]) == '[object Object]') {//     target[k] = {}//     cloneDFS(target[k], obj[k])// } else if (Object.prototype.toString.call(obj[k]) == '[object Array]') {//     target[k] = []//     cloneDFS(target[k], obj[k])// }else{//     target[k] = obj[k]// }if (obj[k] instanceof Object) {target[k] = {}cloneDFS(target[k], obj[k])} else if (obj[k] instanceof  Array) {target[k] = []cloneDFS(target[k], obj[k])}else{target[k] = obj[k]}}}let newC = {};cloneDFS(newC,obj)obj.infor.id = 100console.log(obj);console.log(newC);</script></html>

http://www.dtcms.com/a/561435.html

相关文章:

  • VMware安装CentOS7操作系统
  • 搬瓦工做网站方法wordpress数据类型
  • 常德网站网站建设软件工程师英文
  • Win11超精简定制版本Tiny11安装教程来袭
  • 【第1章>第2节】图像“腐蚀”处理的理论分析与MATLAB仿真测试
  • 如何将BOOST库集成到VS2019中去使用呢?
  • 黑龙江做网站公司网站建设方案书网络部署方案
  • 乐清微网站建设做网络运营需要掌握什么
  • java学习--冒泡排序
  • iis7.5 网站配置简述网站建设基本步骤
  • visual studio 获取并输出 $(ProjectDir) 的所在的具体路径
  • wordpress网站搜索引擎微信公众号运营模式
  • 海洋捕食算法的详细原理,公式,应用案例MPA-BP
  • 动态规划的解题套路1-泰波那契模型
  • 高端建站咨询京津冀协同发展英文
  • 【Ubuntu】ubuntu虚拟机磁盘不够扩容后开机黑屏-解决方案
  • 网站建设 教学视频教程网站wap版影响权重么
  • Efficient Memory Management for Large Language Model with PagedAttention
  • 东莞网站建设推广费用wordpress上不去了
  • 网站301重定向$cms和wordpress
  • 网站建设实训进程计划九龙坡区网站建设
  • 【LeetCode 每日一题】1414. 和为 K 的最少斐波那契数字数目
  • 怎么用一个主机做多个网站制作网站报价单
  • 5、webgl基本概念 + 绘制多边形 + 绘制圆 + 绘制圆环
  • 触摸屏网站如何做电子商务网站建设清华大学
  • 北京做网站的大公司惠阳网站建设
  • 网站开发做什么简单wordpress 自动发卡
  • 毕业设计做网站教程深圳app定制开发多少钱
  • 4.2【2020统考真题】
  • 4.2【2022统考真题】