4. 深拷贝
深拷贝 = 浅拷贝 + 遇到对象时递归拷贝
(1)最简单莫过于字符序列化,再parse反序列化
语法:JSON.parse(JSON.stringify)
后端传回的变量一般用它来拷贝足以应对。只是,
1、会忽略 undefined
2、会忽略 symbol
3、不能序列化函数
4、不能解决循环引用的对象
5、不能正确处理new Date()
6、不能处理正则
undefined、symbol 和函数这三种情况,会直接忽略。拷贝函数会undefined,拷贝正则会Object{},没拷完整。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| let obj = { name: 'muyiy', a: undefined, b: Symbol('muyiy'), c: function() {} } console.log(obj);
let b = JSON.parse(JSON.stringify(obj)); console.log(b);
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| let obj = { a: 1, b: { c: 2, d: 3 } } obj.a = obj.b; obj.b.c = obj.a;
let b = JSON.parse(JSON.stringify(obj));
|
1 2 3 4
| let date = (new Date()).valueOf(); JSON.stringify(date); JSON.parse(JSON.stringify(date));
|
1 2 3 4 5 6 7 8 9 10
| let obj = { name: "muyiy", a: /'123'/ } console.log(obj);
let b = JSON.parse(JSON.stringify(obj)); console.log(b);
|
(2) 简易深拷贝
第一版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
function cloneDeep1(source) { var target = {}; for(var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { if (typeof source[key] === 'object') { target[key] = cloneDeep1(source[key]); } else { target[key] = source[key]; } } } return target; }
|
第二版 (非对象的返回自身以及数组的支持)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| function isObject(obj) { return typeof obj === 'object' && obj != null; } function cloneDeep2(source) { if (!isObject(source)) return source; var target = Array.isArray(source) ? [] : {}; for(var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { if (isObject(source[key])) { target[key] = cloneDeep2(source[key]); } else { target[key] = source[key]; } } } return target; }
function deepclone(obj = {}) { if (typeof obj !== 'object' || obj == null) { return obj } let target
if (obj instanceof Array) { target = [] } else { target = {} }
for (let key in obj) { if (obj.hasOwnPorperty(key)) { target[key] = deepclone(obj[key]) } }
return target }
var b = cloneDeep2(a); console.log(b);
|
第三版 (支持对象循环引用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| function cloneDeep3(source, hash = new WeakMap()) {
if (!isObject(source)) return source; if (hash.has(source)) return hash.get(source); var target = Array.isArray(source) ? [] : {}; hash.set(source, target); for(var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { if (isObject(source[key])) { target[key] = cloneDeep3(source[key], hash); } else { target[key] = source[key]; } } } return target; }
const a = { name: "muyiy", a1: undefined, a2: null, a3: 123, book: {title: "You Don't Know JS", price: "45"}, } a.circleRef = a var b = cloneDeep3(a) console.log(b)
|
第四版 (保存引用关系)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| function cloneDeep3(source, uniqueList) { if (!isObject(source)) return source if (!uniqueList) uniqueList = []
var target = Array.isArray(source) ? [] : {}
var uniqueData = find(uniqueList, source) if(uniqueData) { return uniqueData.target } uniqueList.push({ source: source, target: target })
for (var key in source) { if(Object.prototype.hasOwnProperty.call(source,key)) { if (isObject(source[key])) { target[key] = cloneDeep3(source[key], uniqueList) } else { target[key] = source[key] } } } return target }
var obj1 = {}; var obj2 = {a: obj1, b: obj1};
console.log(obj2.a === obj2.b);
var obj3 = cloneDeep3(obj2); console.log(obj3.a === obj3.b)
|
第五版 (拷贝Symbol类型)
思路就是先查找有没有 Symbol 属性,如果查找到则先遍历处理 Symbol 情况,然后再处理正常情况,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| function cloneDeep4(source, hash = new WeakMap()) {
if (!isObject(source)) return source; if (hash.has(source)) return hash.get(source); let target = Array.isArray(source) ? [] : {}; hash.set(source, target); let symKeys = Object.getOwnPropertySymbols(source); if (symKeys.length) { symKeys.forEach(symKey => { if (isObject(source[symKey])) { target[symKey] = cloneDeep4(source[symKey], hash); } else { target[symKey] = source[symKey]; } }); } for(let key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { if (isObject(source[key])) { target[key] = cloneDeep4(source[key], hash); } else { target[key] = source[key]; } } } return target; }
var sym1 = Symbol("a"); var sym2 = Symbol.for("b");
a[sym1] = "localSymbol"; a[sym2] = "globalSymbol";
var b = cloneDeep4(a); console.log(b);
|
第六版 破解递归爆栈 (待研究@todo)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| function cloneDeep5(x) { const root = {};
const loopList = [ { parent: root, key: undefined, data: x, } ];
while(loopList.length) { const node = loopList.pop(); const parent = node.parent; const key = node.key; const data = node.data;
let res = parent; if (typeof key !== 'undefined') { res = parent[key] = {}; }
for(let k in data) { if (data.hasOwnProperty(k)) { if (typeof data[k] === 'object') { loopList.push({ parent: res, key: k, data: data[k], }); } else { res[k] = data[k]; } } } }
return root; }
|
小语法:
1 Object.keys(..) 返回一个数组,包含所有可枚举属性
2 Object.getOwnPropertyNames(..) 返回一个数组,包含所有属性,无论它们是否可枚举
3 Object.getOwnPropertyDescriptor() 会返回对象的属性的指定描述
4 in && hasOwnProperty
1 2 3 4 5 6 7 8 9 10 11 12 13
| var anotherObject = { a: 1 };
var myObject = Object.create( anotherObject ); myObject.b = 2; ("a" in myObject); ("b" in myObject); myObject.hasOwnProperty( "a" ); myObject.hasOwnProperty( "b" );
|
5 WeakMap
特点:弱引用,需以对象作为key,不打扰垃圾回收机制,可以通过set,get来取值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| const wm1 = new WeakMap(), wm2 = new WeakMap(), wm3 = new WeakMap(); const o1 = {}, o2 = function(){}, o3 = window;
wm1.set(o1, 37); wm1.set(o2, "azerty"); wm2.set(o1, o2); wm2.set(o3, undefined); wm2.set(wm1, wm2);
wm1.get(o2); wm2.get(o2); wm2.get(o3);
wm1.has(o2); wm2.has(o2); wm2.has(o3);
wm3.set(o1, 37); wm3.get(o1);
wm1.has(o1); wm1.delete(o1); wm1.has(o1);
|
参考
MDN Object.assign
wengjq issue
yygmind issue
yygmind 深浅拷贝汇总
展开语法
MDN Object.getOwnPropertyDescription
MDN WeakMap
神奇的weakmap科普贴
颜海镜