在各個社區查找使用原生js實現深克隆的方法,衆說紛紜,大多數實現效果並不理想,在此將各家之言總結一下,得出一個完美的解決方案。php
本文參考如下文章,感興趣者請移步到原文連接。css
const a = 1;
let b = a;
console.log(b);//1
b = 2;
console.log(a);// 1
console.log(b);// 2
複製代碼
const c="1";
let d=c;
console.log(d);//"1"
d="2";
console.log(c);// "1"
console.log(d);//"2"
複製代碼
const x = true;
let y = x;
console.log(y);//true
y = false;
console.log(x);// true
console.log(y);//false
複製代碼
const s1 = Symbol('foo');
let s2 = s1;
console.log(s2);//Symbol(foo)
console.log(s2 === s1);//true
s2 = Symbol('bar');
console.log(s1, s2); //Symbol(foo) Symbol(bar)
console.log(s2 === s1);//false
複製代碼
const m = function () {alert(1); };
let n = m;
n();//1
n = function () {alert(2); };
m();//1
n();//2
複製代碼
let obj = {
a: 1,
b: 2
}
let obj2 = obj;
console.log(obj);//{a: 1,b: 2}
console.log(obj2);//{a: 1,b: 2}
obj.a = 8;
console.log(obj);//{a: 8,b: 2}
console.log(obj2);//{a: 8,b: 2}
//obj2 = obj 是值的引用,指向同一個地址值,其中一個發生變化,會影響另外一個
複製代碼
由於:數組中能夠嵌套對象,對象沒法實現深克隆,數組也不能。html
function Person(name) {
this.name = name;
}
const Jack = new Person('Jack');
const obj = {
a: 1,
b: function (arg) {
console.log('我是獨一無二的,json複製不了我');
},
c: {
d: 3,
e: {
f: [1,[2,[3,[4,[5]]]]],
g: {
h: 5
}
}
},
date: [new Date(1536627600000), new Date(1540047600000)],
reg: new RegExp('\\w+'),
num: [NaN, Infinity, -Infinity],
person: Jack,
};
//json方法克隆
let obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj);
console.log(obj2);
obj.c.e.f = 1000;//改變源對象的值
obj2.c.e.g.h = 2000;//改變克隆對象的值
console.log(obj.c.e.f, obj2.c.e.f);
console.log( obj.c.e.g.h, obj2.c.e.g.h);
複製代碼
運行結果: vue
由此可知 json 克隆方法有下列缺點:ios
//數據初始化
function Person(name) {
this.name = name;
}
const Jack = new Person('Jack');
const obj = {
a: 1,
b: function (arg) {
console.log('複製我,你牛逼');
},
c: {
d: 3,
e: {
f: [1,[2,[3,[4,[5]]]]],
g: {
h: 5
}
}
},
date: [new Date(1536627600000), new Date(1540047600000)],
reg: new RegExp('\\w+'),
num: [NaN, Infinity, -Infinity],
person: Jack,
};
//深克隆實現方法
function deepClone(origin, target) {
const tar = target || {};
for (let item in origin) {
if (origin.hasOwnProperty(item)) {
if (typeof origin[item] === 'object') {
tar[item] = Object.prototype.toString.call(origin[item]) === '[object Array]' ? [] : {};
deepClone(origin[item], tar[item]);
} else {
tar[item] = origin[item];
}
}
}
return tar;
}
//驗證代碼
let obj2 = deepClone(obj, {});
console.log(obj);
console.log(obj2);
obj.c.e.f = 1000;
obj2.c.e.g.h = 2000;
console.log(obj.c.e.f, obj2.c.e.f);
console.log( obj.c.e.g.h, obj2.c.e.g.h);
複製代碼
運行結果:json
由結果可知,該函數仍有不足:所以,該函數雖未能完美實現全部類型的深克隆,但對於平常開發足矣,平常開發通常只須要完成對象或數組的克隆,如不考慮時間對象、RegExp、Error對象和由構造函數生成的對象,該方法徹底足夠。gulp
//數據初始化
function Person(name) {
this.name = name;
}
const Jack = new Person('Jack');
const obj = {
a: 1,
b: function (arg) {
console.log('複製我,你牛逼');
},
c: {
d: 3,
e: {
f: [1,[2,[3,[4,[5]]]]],
g: {
h: 5
}
}
},
date: [new Date(1536627600000), new Date(1540047600000)],
reg: new RegExp('\\w+'),
num: [NaN, Infinity, -Infinity],
person: Jack,
};
//深克隆函數
function deepClone(data) {
const type = this.judgeType(data);
let obj;
if (type === 'Array') {
obj = [];
for (let i = 0, len = data.length; i < len; i++ ) {
obj.push(this.deepClone(data[i]));
}
} else if (type === 'Object') {
obj = new data.constructor ();//可保持繼承鏈,解決該問題:若是obj中的對象是由構造函數生成的,則使用深拷貝後,會丟棄對象的constructor;
for (let key in data) {
obj[key] = this.deepClone(data[key]);//實現深克隆的關鍵
}
} else {
return data;
}
return obj;
}
//判斷類型函數
function judgeType(obj) {
if (obj instanceof Element) {
return 'element';
}
return Object.prototype.toString.call(obj).slice(8,-1);
}
//驗證代碼
let obj2 = deepClone(obj);
console.log(obj);
console.log(obj2);
obj.c.e.f = 1000;
obj2.c.e.g.h = 2000;
console.log(obj.c.e.f, obj2.c.e.f);
console.log( obj.c.e.g.h, obj2.c.e.g.h);
複製代碼
運行結果:數組
由運行結果可知,該方法徹底實現了全部數據類型的深克隆,是解決深克隆的完美方案之一,鑑定完畢!但這代碼量是否是多了些?!其實還能夠再優化,歡迎你們評論優化。//數據初始化
function Person(name) {
this.name = name;
}
const Jack = new Person('Jack');
const obj = {
a: 1,
b: function (arg) {
console.log('複製我,你牛逼');
},
c: {
d: 3,
e: {
f: [1,[2,[3,[4,[5]]]]],
g: {
h: 5
}
}
},
date: [new Date(1536627600000), new Date(1540047600000)],
reg: new RegExp('\\w+'),
num: [NaN, Infinity, -Infinity],
person: Jack,
};
//深克隆函數
function deepClone(obj) {
if (obj === null) return null;
if (typeof obj !== 'object') return obj;
if (obj.constructor === Date) return new Date(obj);
if (obj.constructor === RegExp) return new RegExp(obj);
const newObj = new obj.constructor (); //保持繼承鏈
for (let key in obj) {
if (obj.hasOwnProperty(key)) { //不遍歷其原型鏈上的屬性
const val = obj[key];
newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; // 使用arguments.callee解除與函數名的耦合。
}
}
return newObj;
}
//判斷代碼
const obj2 = deepClone(obj);
console.log(obj);
console.log(obj2);
obj.c.e.f = 1000;
obj2.c.e.g.h = 2000;
console.log(obj.c.e.f, obj2.c.e.f);
console.log( obj.c.e.g.h, obj2.c.e.g.h);
複製代碼
運行結果:bash
相對於上一個完美解決方法,該方法的實現可圈可點,代碼量更少,一樣是達到了各類數據類型深克隆的實現,選擇哪一種方法都是沒問題的,看我的喜愛罷了。網絡
以上是我的參考衆家之言後總結的深克隆方法,若有不正確之處,請各方你們多多指點,謝謝!