预备知识

  • 引用数据类型

    • Object(在JS中除了基本数据类型以外的都是对象,数据是对象,函数是对象等等)
  • 基本数据类型

    • NumberStringBooleanNullUndefinedSymbol
  • 文章参考了牛客网CodeSheep

赋值 vs 浅拷贝 vs 深拷贝

赋值(不能算是拷贝,因为拷贝的仅仅只是引用关系,并没有生成新的实际对象)

很常见的一种

1
2
3
4
5
6
7
8
9
10
11
12
var a = 10;
var b = 100;
var obj = {
name:"李白",
sex:"男"
}
// 注意,这个严格来说是赋值,不是什么浅拷贝深拷贝!后面有原因
var obj2 = obj;

// 注意,这个严格来说是赋值,不是什么浅拷贝深拷贝
Student codeSheep = new Student();
Student codePig = codeSheep;

浅拷贝

一个误区

很多人说var objOrigin = {name:'李白'}; var objAfter = objOrigin; 是浅拷贝,严格来说是错误的!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 严格来说是错误的!
// 如果说下面这一行是浅拷贝
var objOrigin = {name:'李白'}; var objAfter = objOrigin;是浅拷贝

//那么这个是什么?
var obj1 = { name: '李白', sex: '男' };

var clonedObj = { ...obj1 };
clonedObj.name="李黑";

console.log(clonedObj); // {name: '李黑', sex: '男'}

console.log(obj1);// {name: '李白', sex: '男'}

//可以看到,克隆后的对象将名字修改为了李黑,原来的没有变化
  • 这里使用了展开运算符(扩展运算符),官方解释是浅拷贝

  • 如果按照之前有些人的说法,下面这个是浅拷贝

    1
    var objOrigin = {name:'李白'}; var objAfter = objOrigin;
    • 那么mdnWebDocs当中的展开运算符(扩展运算符)可以实现浅拷贝,那么按道理来说这个所谓的浅拷贝,修改了浅拷贝后的对象不会导致原来的对象改变吧?那为什么修改后会发生改变?

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      //var objOrigin = {name:'李白'}; var objAfter = obj;是浅拷贝

      var objOrigin = {name:'李白'};
      var objAfter = objOrigin;
      //修改拷贝后的对象当中的name(name为基本数据类型)
      objAfter.name="李黑";

      //输出为 {name: '李黑'}
      console.log(objAfter);

      //输出为 {name: '李黑'}
      console.log(objOrigin);

    • 所以觉得,赋值就是赋值,不是什么浅拷贝和深拷贝!不然你说var objAfter = objOrigin就是浅拷贝,那么为什么修改了objAfterobjOrigin也会变化?并且修改的还是对象当中的基本数据类型!(因为后面深拷贝还设计到对象当中的数据类型)

真正的浅拷贝

  • 比如我们试图通过studen1实例,拷贝得到student2,如果是浅拷贝这种方式,大致模型可以示意成如下所示的样子:

  • 很明显,值类型的字段会复制一份,而引用类型的字段拷贝的仅仅是引用地址,而该引用地址指向的实际对象空间其实只有一份。
  • 所以浅拷贝是什么,就是赋值引用数据类型当中存储的基本数据类型,而引用数据类型当中的引用数据类型(比如数组当中嵌套对象的情况),仅仅只是将地址赋值给了另外一个对象!

深拷贝

  • 深拷贝相较于上面所示的浅拷贝,除了值类型字段会复制一份,引用类型字段所指向的对象,会在内存中也创建一个副本,就像这个样子:

  • 所以深拷贝是什么,就是不管对象当中嵌套了多少层引用数据类型还是基本数据类型,都建立一个新的给自己

一些常用的深浅拷贝方法

浅拷贝

展开运算符(扩展运算符)

  • 可以看到,除了基本数据类型拷贝了,里面的引用数据类型并未进行拷贝,修改一个拷贝后的引用数据类型会影响原来的
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
var objOrigin = {
name: '李白',
sex: '男',
other: {
hobby: "喝酒",
food: "吃肉"
}
};
//扩展运算符浅拷贝
var objAfter = {
...objOrigin
}
//false
console.log(objAfter === objOrigin);

//true (浅拷贝的原因)
console.log(objAfter.other === objOrigin.other);

//修改
objAfter.name = "动感超人";

//修改2
objAfter.other.food = "蔬菜"

//{ name: '动感超人', sex: '男', other: { hobby: '喝酒', food: '蔬菜' } }
console.log(objAfter);

// { name: '李白', sex: '男', other: { hobby: '喝酒', food: '蔬菜' } }
console.log(objOrigin);

Object.assign

  • 可以看到,除了基本数据类型拷贝了,里面的引用数据类型并未进行拷贝,修改一个拷贝后的引用数据类型会影响原来的
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 objOrigin = {
name: '李白',
sex: '男',
other: {
hobby: "喝酒",
food: "吃肉"
}
};
//Object.assign浅拷贝
var objAfter = {};
Object.assign(objAfter,objOrigin)
//false
console.log(objAfter === objOrigin);

//true (浅拷贝的原因)
console.log(objAfter.other === objOrigin.other);

//修改
objAfter.name = "动感超人";

//修改2
objAfter.other.food = "蔬菜"

//{ name: '动感超人', sex: '男', other: { hobby: '喝酒', food: '蔬菜' } }
console.log(objAfter);

// { name: '李白', sex: '男', other: { hobby: '喝酒', food: '蔬菜' } }
console.log(objOrigin);

深拷贝

JSON.stringify和JSON.parseInt实现

  • 可以看到,除了基本数据类型拷贝了,里面的引用数据类型也进行了拷贝,修改一个拷贝后的引用数据类型不影响原来的
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
var objOrigin = {
name: '李白',
sex: '男',
other: {
hobby: "喝酒",
food: "吃肉"
}
};
//JSON.parse和JSON.stringify 深拷贝
var objAfter = JSON.parse(JSON.stringify(objOrigin));
//false
console.log(objAfter === objOrigin);

//true (浅拷贝的原因)
console.log(objAfter.other === objOrigin.other);

//修改
objAfter.name = "动感超人";

//修改2
objAfter.other.food = "蔬菜"

//{ name: '动感超人', sex: '男', other: { hobby: '喝酒', food: '蔬菜' } }
console.log(objAfter);

// { name: '李白', sex: '男', other: { hobby: '喝酒', food: '吃肉' } }
console.log(objOrigin);

lodash当中的cloneDeep

cloneDeep当中的API地址

1
2
3
4
5
6
var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
//比较是否相同 返回 false
console.log(deep[0] === objects[0]);