js實現深拷貝
阿新 • • 發佈:2020-09-10
目錄
- JS實現
- 簡單深拷貝(一層淺拷貝)
- 粗暴深拷貝(拋棄物件的constructor)
- 複雜深拷貝(相對完美)
- ES實現
- lodash中的cloneDeep方法(完美)
一.JS實現
1.簡單深拷貝(一層淺拷貝)
①for迴圈拷貝
// 只複製第一層的淺拷貝 function simpleCopy(obj1) { var obj2 = Array.isArray(obj1) ? [] : {}; for (let i in obj1) { obj2[i] = obj1[i]; } return obj2; } var obj1 = { a: 1, b: 2, c: { d: 3 } } var obj2 = simpleCopy(obj1); obj2.a = 3; obj2.c.d = 4; alert(obj1.a); // 1 alert(obj2.a); // 3 alert(obj1.c.d); // 4 alert(obj2.c.d); // 4
②Object.assign()實現一層深拷貝
var obj1 = {
a: 1,
b: 2,
c: 3
}
var obj2 = Object.assign({}, obj1);
obj2.b = 5;
console.log(obj1.b); // 2
console.log(obj2.b); // 5
var obj1 = { a: 1, b: 2, c: ['a','b','c'] } var obj2 = Object.assign({}, obj1); obj2.c[1] = 5; console.log(obj1.c); // ["a", 5, "c"] console.log(obj2.c); // ["a", 5, "c"]
③slice實現
// 對只有一級屬性值的陣列物件使用slice
var a = [1,2,3,4];
var b = a.slice();
b[0] = 2;
alert(a); // 1,2,3,4
alert(b); // 2,2,3,4
// 對有多層屬性的陣列物件使用slice
var a = [1,[1,2],3,4];
var b = a.slice();
b[1][0] = 2;
alert(a); // 1,2,2,3,4
alert(b); // 1,2,2,3,4
④使用concat()方法
var a=[1,2,[3,4]] var c=[]; var b=c.concat(a); b[0]=5; b[2][0]=6; console.log(b[0]);//5 console.log(a[0])//1 console.log(b[2][0]);//6 console.log(a[2][0])//6
⑤es6的擴充套件運算子"..."
var a=[1,2,[3,4]]
var b=[...a];
b[0]=5;
b[2][0]=6
console.log(b[0]);//5
console.log(a[0])//1
console.log(b[2][0]);//6
console.log(a[2][0])//6
⑥通過Object.create()實現
function deepCopy(obj) {
var copy = Object.create(Object.getPrototypeOf(obj));
var propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(name) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
Object.defineProperty(copy, name, desc);
});
return copy;
}
var obj1 = { a: 1, b: {bc: 50, dc: 100, be: {bea: 1}} };
var obj2 = deepCopy(obj1);
obj2.a = 20;
obj2.b.bc = 60;
console.log(obj1.a)//1
console.log(obj2.a)//20
console.log(obj1.b.bc)//60
console.log(obj2.b.bc)//60
2.粗暴深拷貝(拋棄物件的constructor)
使用JSON.stringify和JSON.parse實現深拷貝:JSON.stringify把物件轉成字串,再用JSON.parse把字串轉成新的物件;
function deepCopy(obj1){
let _obj = JSON.stringify(obj1);
let obj2 = JSON.parse(_obj);
return obj2;
}
var a = [1, [1, 2], 3, 4];
var b = deepCopy(a);
b[1][0] = 2;
alert(a); // 1,1,2,3,4
alert(b); // 2,2,2,3,4
缺陷:它會拋棄物件的constructor,深拷貝之後,不管這個物件原來的建構函式是什麼,在深拷貝之後都會變成Object;這種方法能正確處理的物件只有 Number, String, Boolean, Array, 扁平物件,也就是說,只有可以轉成JSON格式的物件才可以這樣用,像function沒辦法轉成JSON;
let obj1 = {
fun:function(){
alert(123);
}
}
let obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun); // function
console.log(typeof obj2.fun); // undefined
3.複雜深拷貝(相對完美)
遞迴拷貝實現深拷貝,解決迴圈引用問題
/**
* 判斷是否是基本資料型別
* @param value
*/
function isPrimitive(value){
return (typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'symbol' ||
typeof value === 'boolean')
}
/**
* 判斷是否是一個js物件
* @param value
*/
function isObject(value){
return Object.prototype.toString.call(value) === "[object Object]"
}
/**
* 深拷貝一個值
* @param value
*/
function cloneDeep(value){
// 記錄被拷貝的值,避免迴圈引用的出現
let memo = {};
function baseClone(value){
let res;
// 如果是基本資料型別,則直接返回
if(isPrimitive(value)){
return value;
// 如果是引用資料型別,我們淺拷貝一個新值來代替原來的值
}else if(Array.isArray(value)){
res = [...value];
}else if(isObject(value)){
res = {...value};
}
// 檢測我們淺拷貝的這個物件的屬性值有沒有是引用資料型別。如果是,則遞迴拷貝
Reflect.ownKeys(res).forEach(key=>{
if(typeof res[key] === "object" && res[key]!== null){
//此處我們用memo來記錄已經被拷貝過的引用地址。以此來解決迴圈引用的問題
if(memo[res[key]]){
res[key] = memo[res[key]];
}else{
memo[res[key]] = res[key];
res[key] = baseClone(res[key])
}
}
})
return res;
}
return baseClone(value)
}
二.ES外掛lodash
import lodash from 'lodash'
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = lodash.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
站在巨人肩膀上摘蘋果
https://www.jianshu.com/p/cf1e9d7e94fb
https://www.jianshu.com/p/5f6cd3dabc1c
https://segmentfault.com/a/1190000015455662
https://segmentfault.com/a/1190000018371840