js深浅拷贝梳理

const originArray = [1, 2, [6, 7], 3, 4, 5]; let shallowCopyArr = []; let deepCopyArr = []; const originObject = { a: 1, b: {c: 3} }; let shallowCopyObj = {}; let deepCopyObj = {}const arrayLike = { 0: "AAA", 1: [1, 2], 2: "BBB", length: 3 }; let shallowCopyArrLike = []; let deepCopyArrLike = [];

1. 浅拷贝
引用的复制
  • 浅拷贝实现:赋值操作符
  • "首层浅拷贝"实现:
    对象:遍历、Object.assign、扩展运算符
    数组(类数组对象):遍历、扩展运算符、Array.from、slice、concat、filter、map、reduce等数组方法
    (注意:所有的数组标准方法都适用于数组和类数组对象,只有concat例外, 具体使用参考js伪数组对象)
对象以遍历为例,数组以concat为例
for (const prop in originObject) { if (originObject.hasOwnProperty(prop)) { shallowCopyObj[prop] = originObject[prop]; } } shallowCopyObj.a = 2; shallowCopyObj.b.c = 4; shallowCopyObj// { a: 2, b: {c: 4} } originObject// { a: 1, b: {c: 4} }----------arrayLike[Symbol.isConcatSpreadable] = true; shallowCopyArrLike = [].concat.call(arrayLike, [7, 8]); shallowCopyArrLike.push(8) shallowCopyArrLike[2].push(3) shallowCopyArrLike// ['AAA', [1,2,3], 'BBB', 7, 8, 3] arrayLike // const arrayLike = { //0: "AAA", //1: [1, 2, 3], //2: "BBB", //length: 3 // };

2. 深拷贝
堆内存的重新分配
【js深浅拷贝梳理】实现:对象、数组、类数组对象通用
  • 递归
  • JSON.parse, JSON.stringify
  • jquery.extend()
JSON.parse, JSON.stringify实现:
const originObject = { a: 1, b: { c: 3 }, d: undefined, e: Array, f: Symbol('foo') }; const originArray = [1, 2, [6, 7], 3, 4, 5, undefined, Array]; deepCopyObj = JSON.parse(JSON.stringify(originObject)); deepCopyArr = JSON.parse(JSON.stringify(originArray)); deepCopyObj.a = 2; deepCopyObj.b.c = 4; deepCopyObj// { a: 2, b: {c: 4} } deepCopyArr// [1, 2, [6, 7], 3, 4, 5, null, null]实现限制: 1. 非数组对象的属性值为undefined、函数、Symbol值时在序列化时被忽略, 数组中会转化为null 2. 无法处理循环引用的对象

递归实现:
关键点在于
  • 拷贝环问题
  • Date、Reg等引用类型的拷贝
  • 函数的拷贝
(1) 解决拷贝环问题: 使用Map/WeakMap,推荐使用WeakMap
const obj = { a:1, c: [1,2], d: { e: 1, }, f: null, }; obj.g = obj; function isObject(target) { return (typeof target === 'object' || typeof target === 'function') && target !== null } function clone(target) { // WeakMap对键名的引用为弱引用,有利于垃圾回收机制 const vm = new WeakMap(); function deepClone(target) { if (isObject(target)) { if (vm.has(target)) return vm.get(target); let cloneTarget = Array.isArray(target) ? [] : {}; vm.set(target, cloneTarget); for (const key in target) { cloneTarget[key] = deepClone(target[key]); } return cloneTarget; } else { return target; } }; return deepClone(target); } const obj1 = clone(obj) obj1.c[1] = 5; obj1.f = [1,2]; console.log(obj1)// {a: 1, c: [1,5], d: {e: 1}, f: [1,2], g: {...} }

(2) Date、Reg等引用类型的拷贝
使用constructor/Object.prototype.toString.call判断类型,根据不同引用类型做相应处理
未完待续........
(3) 函数、不可枚举属性等的拷贝
有这时间研究不如使用现成的工具库lodash.cloneDeep

    推荐阅读