深入JavaScript Day33 - 序列化实现深拷贝的缺点分析、手写深拷贝、手写事件总线简单版

一、序列化实现深拷贝的缺点、手写深拷贝

1、借助序列化实现深拷贝,有哪些缺点?

  • ①不能拷贝函数
  • ②不能拷贝Symbol()
  • ③不能拷贝循环引用的对象(会报错,比如window.window.window.window)
  • ④key的值为 undefined不会拷贝(但是感觉问题不大)
const _ = require("lodash");
const { cloneDeep } = require("./copy");

let s1 = Symbol("aaa");
let s2 = Symbol("bbb");

const obj = {
  name: "why",
  age: 18,
  friend: {
    name: "kobe",
    friend: {
      name: "james",
    },
  },
  hobbies: ["run", "swing"],
  food: null,
  country: undefined,
  [s1]: "aaa",
  [s2]: s2,
  run: function () {
    console.log(this.name, "在跑步");
  },
};
// obj.obj = obj;

const copyObj = JSON.parse(JSON.stringify(obj));
// const copyObj = _.cloneDeep(obj);
// const copyObj = cloneDeep(obj);

// 测试代码
copyObj.name = "copy" + obj.name;
copyObj.friend.name = "copy" + copyObj.friend.name;
copyObj.hobbies.push("eat");
console.log("====================obj======================");
console.log(obj);
console.log("====================copyObj======================");
console.log(copyObj);

// obj.run();
// copyObj.run();
// console.log(obj[s2] === copyObj[s2]);

  • 【序列化深拷贝】输出结果
====================obj======================
{
  name: 'why',
  age: 18,
  friend: { name: 'kobe', friend: { name: 'james' } },
  hobbies: [ 'run', 'swing' ],
  food: null,
  country: undefined,
  run: [Function: run],
  [Symbol(aaa)]: 'aaa',
  [Symbol(bbb)]: Symbol(bbb)
}
====================copyObj======================
{
  name: 'copywhy',
  age: 18,
  friend: { name: 'copykobe', friend: { name: 'james' } },
  hobbies: [ 'run', 'swing', 'eat' ],
  food: null
}

2、实现对象的深拷贝要注意哪些问题?

  • ①对象类型需要递归拷贝
  • ②数组和对象类型需要做区分
  • ③函数本就是为了实现代码复用,所以函数不需要拷贝,直接再次引用即可
  • ④对于symbol类型,使用 for in 语法无法获取,可以使用 Object.getOwnPropertySymbols(obj) 语法
  • ⑤对于 obj.obj.obj === obj 这种循环引用,可以使用WeakMap存一份,然后有值直接进行引用赋值即可。避免死循环
// copy.js
function cloneDeep(obj) {
  let map = new WeakMap();
  return _cloneDeep(obj, map);
}

function _cloneDeep(obj, map) {
  if (
    typeof obj !== "object" ||
    obj === null ||
    typeof obj === "function" ||
    typeof obj === "symbol"
  ) {
    return obj;
  }

  if (map.has(obj)) return map.get(obj);

  let copyObj = null;
  if (Array.isArray(obj)) {
    copyObj = [];
  } else {
    copyObj = {};
  }
  map.set(obj, copyObj);

  for (const key in obj) {
    copyObj[key] = _cloneDeep(obj[key], map);
  }

  for (const key of Object.getOwnPropertySymbols(obj)) {
    copyObj[key] = _cloneDeep(obj[key], map);
  }

  return copyObj;
}

module.exports = {
  cloneDeep,
};

3、手写事件总线,实现 emmit、on、off 三个方法

  • 核心【map里面包list】
class EventBus {
  constructor() {
    this.evnentStore = {};
  }

  on(eventName, callback) {
    let list = this.evnentStore[eventName];
    if (!list) {
      list = [];
      this.evnentStore[eventName] = list;
    }
    if (list.indexOf(callback) < 0) list.push(callback);
  }
  off(eventName, callback) {
    let list = this.evnentStore[eventName];
    if (!list) return;
    let index = list.indexOf(callback);
    if (index >= 0) list.splice(index, 1);
  }
  emmit(eventName) {
    let list = this.evnentStore[eventName];
    if (!list) return;
    for (const item of list) {
      item();
    }
  }
}

const eventBus = new EventBus();

// 监听一
const func1 = function () {
  console.log("function1 listen to aaa");
};
eventBus.on("aaa", func1);

// 监听二
const func2 = function () {
  console.log("function2 listen to aaa");
};
eventBus.on("aaa", func2);

// 监听三
const func3 = function () {
  console.log("function3 listen to bbb");
};
eventBus.on("bbb", func3);

// 触发事件
eventBus.emmit("aaa");

// 移除事件
eventBus.off("aaa", func2);

// 触发事件
eventBus.emmit("aaa");

你可能感兴趣的:(深入JavaScript Day33 - 序列化实现深拷贝的缺点分析、手写深拷贝、手写事件总线简单版)