导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

冷门技能

var obj = {
	name: 'Lance',
  age: 28,
  say() {
  	console.log('Hello');
  },
  a: {
  	b: {
    	c: 233
    }
  }
}

ES5

function deepClone(obj) {
    // 处理 null 或者 undefined
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }

    // 处理数组
    if (Array.isArray(obj)) {
        var arrCopy = [];
        for (var i = 0; i < obj.length; i++) {
            arrCopy[i] = deepClone(obj[i]);
        }
        return arrCopy;
    }

    // 处理普通对象
    var objCopy = {};
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            objCopy[key] = deepClone(obj[key]);
        }
    }
    return objCopy;
}

WeakMap & Map & Object

Map 键名 -> 任意类型 {}、[]都行

{} 键名 -> 字符串

WeakMap 键名 -> 只能是对象

WeakMap 使用场景

const oBtn1 = document.querySelector('#btn1');
const oBtn2 = document.querySelector('#btn2');

oBtn1.addEventListener('click', handleBtn1Click, false);
oBtn2.addEventListener('click', handleBtn2Click, false);

function handleBtn1Click() {}
function handleBtn2Click() {}

// 某些场景下需要删除btn
oBtn1.remove();
oBtn2.remove();

// 而此刻 handleBtn1Click 不会被销毁,理论上需要手动销毁
handleBtn1Click = null;
handleBtn2Click = null;

有了 WeakMap 就可以这样做:

const oBtnMap = new WeakMap();
oBtnMap.set(oBtn1, handleBtn1Click);
oBtnMap.set(oBtn2, handleBtn2Click);

oBtn1.addEventListener('click', oBtnMap.get(oBtn1), false);
oBtn2.addEventListener('click', oBtnMap.get(oBtn2), false);

oBtn1.remove();
oBtn2.remove();

WeakMap 是弱引用,一旦 oBtn1 被 remove 掉,它就会被回收。所以这也解释了为什么 WeakMap 的键名要是个对象类型。

oBtn1 都被回收了,相应的键值 handleBtn1Click 也会被回收

⭐️ 总结

WeakMap 中键名的对象,在外界没有被引用了,就会被回收,同时一并的把相应的键值也给回收了。

ES6

前置知识

undefined == null

var a = undefined;
a == null; // true
a === undefined; // true

⭐️ 通过 obj.constructor 构造器创建新对象

var arr = [];
var newArr = new arr.constructor();
newArr.push(1);
console.log(arr);
console.log(newArr);

Untitled

var obj = {};
var newObj = new obj.constructor();
newObj.name = 'Lance';
console.log(obj);
console.log(newObj);

Untitled

function deepClone(source) {
  if (source == undefined || typeof source !== 'object') {
    return source;
  }
  if (source instanceof Date) {
    return new Date(source);
  }
  if (source instanceof RegExp) {
    return new RegExp(source);
  }

  // 创建一个和源对象一样的空对象
  const target = new source.constructor();

  // 然后把源对象中自身可枚举属性一个个填入空对象
  for (const key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      target[key] = deepClone(source[key]);
    }
  }
  return target;
}

⭐️ 解决循环引用

var test1 = {};
var test2 = {};
test1.test2 = test2;
test2.test1 = test1;

function deepClone(source, hashMap = new WeakMap()) {
  if (source == undefined || typeof source !== 'object') {
  	return source;
  }
  if (source instanceof Date) {
  	return new Date(source);
  }
  if (source instanceof RegExp) {
  	return new RegExp(source);
  }

  const hasItem = hashMap.get(source);
  if (hasItem) return hasItem;

  const target = new source.constructor();
  hashMap.set(source, target);

  for (const key in source) {
  	if (Object.prototype.hasOwnProperty.call(source, key)) {
    	target[key] = deepClone(source[key], hashMap);
    }
  }
  return target;
}

console.log(deepClone(test2));