如何拷贝一个js对象,拷贝对象分为拷贝地址、浅拷贝和深拷贝。
基本概念
js对象作为引用类型不同于基本类型,基本数据类型的拷贝直接用 =
赋值即可。
声明一个js变量,并将一个对象赋值给变量,变量此时其实存储的是变量的地址而不是变量的内容。
故而,将一个对象变量赋值给另一个变量其实传的是地址,而不是拷贝了一个新的对象。
此时原变量以及新变量其实都指向同一个内存地址。
基本类型保存值,引用类型保存指针。
比如这样:
那么如何拷贝一个对象呢?
浅拷贝
浅拷贝会申请一块新的内存空间存放对象,但是只能拷贝到原对象的第一层中的基本数据类型,如果原对象第一层中含有对象则拷贝到的这个对象的对象成员依然是地址。
Object.assign()
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
会创建新的内存空间,并拷贝第一层,在深层拷贝的依然是地址。
1
| var o1 = Object.assign({}, obj);
|
展开运算符
展开运算符 ...arr
作用是把数组展开。
可以使用展开运算符将数组展开、合并数组、合并对象、传参等等。
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
| var arr = [1, 2, 3, 4, 5]; console.log(...arr);
var arr = [1, 2, 3, 4, 5]; var arr2 = [...arr, 6];
var obj = { name: 'mason' }; var o1 = { ...obj, age: 22 };
var arr = [1, 2, 3];
function func(a, b, c) { console.log(a); console.log(b); console.log(c); } func(...arr);
|
会创建新的内存空间,并拷贝第一层,在深层拷贝的依然是地址。
深拷贝
深拷贝就是从内存中开辟新的一块空间用于存放新对象,新的对象与原有对象完全独立并且和原对象一模一样。
使用JSON方法
先 JSON.stringify()
再 JSON.parse()
,会忽略 undefined
和函数,无法拷贝原型链上的属性和方法,层级过深会栈溢出。
1 2 3 4
| var obj = { name: 'mason' }; var o1 = JSON.parse(JSON.stringify(obj));
|
DFS
无法保持引用,层级很深会栈溢出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function deepCopy(obj) { let target = Array.isArray(obj) ? [] : {};
for (var k in obj) { if (obj.hasOwnProperty(k)) { if (typeof obj[k] === 'object') { target[k] = deepCopy(obj[k]); } else { target[k] = obj[k]; } } } 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
| function cloneLoop(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; }
|