拷贝一个js对象

拷贝一个js对象

如何拷贝一个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);
// 1 2 3 4 5

// 合并数组
var arr = [1, 2, 3, 4, 5];
var arr2 = [...arr, 6];
// 1 2 3 4 5 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);

会创建新的内存空间,并拷贝第一层,在深层拷贝的依然是地址。

1
2
3
var o1 = {
...obj
};

深拷贝

深拷贝就是从内存中开辟新的一块空间用于存放新对象,新的对象与原有对象完全独立并且和原对象一模一样。

使用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) {
// 根据传入的是对象还是数组初始化目标对象target
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;

// 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
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;
}

评论