深浅拷贝

# 深浅拷贝

[TOC]

# 一、复制变量值

# 1.1 复制基本类型值

如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。

let a = 1;
let b = a;
b = 3;
console.log(a);	// 1

let m = Symbol(2);
let n = m;
n = Symbol(3);
console.log(n);	// Symbol(3)
1
2
3
4
5
6
7
8
9

# 1.2 复制引用类型值

赋值不仅是值的复制,也是引用的传递。

let obj1 = new Object();
let obj2 = obj1;
obj1.name = ‘Lin';
console.log(obj2.name);	// Lin

// 对象类型 : 值和引用都相同才行
let a = [ 1, 2, 3 ];
let b = [ 1, 2, 3 ];
alert( a == b );  //false  

let c = [ 1, 2 ];
let d = c;
d[0] = 2;
console.log(c);	// [ 2, 2 ];
console.log(d);	// [ 2, 2 ];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

复制引用类型值 参考教程:看看你知道的“浅拷贝”是对的吗 (opens new window)

和原数据是否指向同一对象 第一层数据为基本数据类型 原数据中包含子对象
赋值 改变会使原数据一同改变 改变会使原数据一同改变
浅拷贝 改变会使原数据一同改变 改变会使原数据一同改变
深拷贝 改变会使原数据一同改变 改变会使原数据一同改变

# 二、克隆对象

# 2.1 浅拷贝

复制已有对象中非对象属性的值和对象属性的引用。

**敲重点,只有对象属性的引用才会被复制。**对象属性的值不会被引用。

# 2.1.1 Arrary.prototype.slice()

// MDN 明确为浅拷贝的 Arrary.prototype.slice()
let a = [1, 2, { x: 1 }];
let b = Array.prototype.slice.call(a);
b[0] = 2;

// b非对象属性的改变显然没有引起a的改变。
console.log(a);	// [1, 2, { x: 1 }]
console.log(b);	// [2, 2, { x: 1 }]

// b对象属性的值改变会引起a的改变。
b[2].x = 2;
console.log(a);	// [1, 2, { x: 2 }]
console.log(b);	// [2, 2, { x: 2 }]
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2.1.2 Array.prototype.concat()

  • 同上。

# 2.1.3 Object.assign()

由于 Object.assign(..) 就是使用 = 操作符来赋值,所以源对象属性的一些特性(比如 writable)不会被复制到目标对象。

需注意的是目标对象只有一层的时候,是深拷贝。

// 将所有可枚举属性的值从一个或多个源对象复制(使用 = 操作符赋值)到目标对象。它将返回目标对象。
let a = { x: 1 };
let b = { y: 2 };
let c = Object.assign( a, b );
c.z = 3;
console.log(a);	// { x: 1, y: 2, z: 3 }
console.log(b); // { y: 2 }
console.log(c);	// { x: 1, y: 2, z: 3 }
1
2
3
4
5
6
7
8

# 2.1.4 手写 shallowCopy

function shallowCopy(a) {
    let b = Array.isArray(a) ? [] : {};
    for ( let i in a){
        b[i] = a[i];
    }
    return b;
} 
1
2
3
4
5
6
7

# 2.2 深拷贝

复制已有对象中所有的属性的值及对象属性中的属性值,无论嵌套了多少层。

# 2.2.1 手写deepCopy()

let obj = {
    name: "reci",
    age: 18,
    skill: {
        做饭: "满汉全席",
        音乐: "飙歌小能手",
        乐器: ["钢琴","古筝","吉他"] 
    }
};
function deepCopy(data){
    if(typeof data === "object" && data){ 
        let val = Array.isArray(data) ? []:{};
        for(let i in data){
           // 如果里面有嵌套,就继续递归
           val[i] = deepCopy(data[i]);
        }
        return val;
    } else {
        return data
    }
}    

// 完全相同又相互独立
let newObj = deepCopy(obj);
console.log(obj == newObj);									//false
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

# 2.2.2 JSON.parse(JSON.stringify())

// 要求所要复制的对象的属性值非函数
// 需要保证对象是 JSON 安全的(也就是说可以被序列化为一个 JSON 字符串并且可以根据这个字符串解析出一个结构和值完全一样的对象),所以只适用于部分情况。
let b = JSON.parse(JSON.stringify(a));
1
2
3

如果需要属性值是函数或者是undefined,就会被过滤掉。

# 2.2.3 利用扩展运算符拷贝数组

let b = [...a]
1