← 返回目录

第 6 卷

第 6 / 8 卷日期:

57// 测试示例 58// 1. 基本深拷贝测试(嵌套对象)

59const obj1 = { 60name: '深拷贝',

61user: { age: 20 }, 62hobbies: ['篮球', '游戏'],

63birth: new Date('2000-01-01'), 64reg: /test/g

65};

66const obj2 = deepClone(obj1);

67obj2.user.age = 21;

68obj2.hobbies.push('编程');

69obj2.birth.setFullYear(2001);

70console.log(obj1.user.age); // 20(不影响原对象)
71console.log(obj1.hobbies); // ['篮球', '游戏'](不影响)
72console.log(obj1.birth.getFullYear()); // 2000(不影响)
73console.log(obj2.reg.flags); // 'g'(RegExp拷贝成功)

74 75// 2. 循环引用测试(obj.a = obj)

76const obj3 = { a: 1 };

77obj3.b = obj3; //循环引用:obj3.b指向自身
78const obj4 = deepClone(obj3);
79console.log(obj4.a); // 1
80console.log(obj4.b === obj4); // true(拷贝后仍保持循环引用,且不报错)

81 82// 3. Symbol属性测试

83const sym = Symbol('test');

84const obj5 = { [sym]: 'symbol值' };

85const obj6 = deepClone(obj5);

86console.log(obj6[sym]); // 'symbol值'(Symbol属性拷贝成功)

87

补充总结(新增5道面试题)

  1. 防抖(debounce):延迟执行,频繁触发时只执行最后一次,核心是清除定时器重新计时,支持

立即执行和取消

  1. 节流(throttle):控制执行频率,指定时间内只执行一次,结合时间戳和定时器实现完整效果,

支持取消

  1. Vue响应式(Vue2):核心是数据劫持(Object.defineProperty)+依赖收集(Dep)+观察者

(Watcher),递归劫持对象属性,数据变化时触发视图更新

十一、手写事件总线(EventBus)的代码实现

核心原理

  1. 事件总线是一种发布-订阅模式的实现,用于组件/模块间的解耦通信,核心是维护一个事件注册表
  2. 事件注册表:以事件名为键,对应的值是一个数组,存储该事件的所有订阅回调函数
  3. 核心方法:
  • on:订阅事件,向注册表中添加回调函数
  • emit:发布事件,触发对应事件的所有回调函数并传递参数
  • off:取消订阅,从注册表中移除指定回调函数(若未指定则移除该事件所有回调)
  • once:订阅一次性事件,回调函数执行一次后自动取消订阅
  1. 需要处理边界情况:重复订阅同一回调、取消不存在的事件/回调、触发未订阅的事件

实现代码

Code block

1class EventBus { 2constructor() {

3//初始化事件注册表:key-事件名,value-回调函数数组
4this.events = {};
5}

6 7/**

8* 订阅事件

9* @param {string} eventName -事件名
10* @param {Function} callback -订阅回调
11*/
12on(eventName, callback) {
13//校验参数:事件名必须是字符串,回调必须是函数
14if (typeof eventName !== 'string' || typeof callback !== 'function') {
15throw new Error('事件名必须为字符串,回调必须为函数');
16}
17//若事件不存在,初始化回调数组
18if (!this.events[eventName]) {
19this.events[eventName] = [];
21//避免重复添加同一回调
22if (!this.events[eventName].includes(callback)) {
29* @param {string} eventName -事件名
30* @param {...any} args -传递给回调的参数
31*/
32emit(eventName, ...args) {
33if (typeof eventName !== 'string') {
34throw new Error('事件名必须为字符串');
35}
36//若事件不存在,直接返回
37if (!this.events[eventName]) return;
38//触发所有回调,绑定this为EventBus实例(可选,便于回调内调用其他方法)
39this.events[eventName].forEach(callback => {
40callback.apply(this, args);
41});
42}

43 44/**

45* 取消订阅 46* @param {string} eventName - 事件名

47* @param {Function} [callback] -可选,要取消的回调;未传递则移除该事件所有回调
48*/
49off(eventName, callback) {
50if (typeof eventName !== 'string') {
51throw new Error('事件名必须为字符串');
52}
53//事件不存在,直接返回
54if (!this.events[eventName]) return;
55//未指定回调:移除该事件所有订阅
56if (!callback) {
57this.events[eventName] = [];
58return;
59}
60//指定回调:只移除该回调(过滤掉目标回调)
61if (typeof callback === 'function') {
62this.events[eventName] = this.events[eventName].filter(cb => cb

!== callback);

63} 64}

65 66/**

67*订阅一次性事件(执行一次后自动取消)
68* @param {string} eventName -事件名
69* @param {Function} callback -订阅回调
全站分卷索引 · 8 卷
  1. 1第 1 卷
  2. 2第 2 卷
  3. 3第 3 卷
  4. 4第 4 卷
  5. 5第 5 卷
  6. 6第 6 卷
  7. 7第 7 卷
  8. 8第 8 卷