文章目录
预备知识
-
引用数据类型
- Object(在JS中除了基本数据类型以外的都是对象,数据是对象,函数是对象等等)
-
基本数据类型
Number
、String
、Boolean
、Null
、Undefined
、Symbol
-
文章参考了牛客网CodeSheep
赋值 vs 浅拷贝 vs 深拷贝
赋值(不能算是拷贝,因为拷贝的仅仅只是引用关系,并没有生成新的实际对象)
很常见的一种
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; 是浅拷贝,严格来说是错误的!
// 严格来说是错误的!
// 如果说下面这一行是浅拷贝
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: '男'}
//可以看到,克隆后的对象将名字修改为了李黑,原来的没有变化
- 这里使用了展开运算符(扩展运算符),官方解释是浅拷贝
-
如果按照之前有些人的说法,下面这个是浅拷贝
var objOrigin = {name:'李白'}; var objAfter = objOrigin;
-
那么
mdnWebDocs
当中的展开运算符(扩展运算符)可以实现浅拷贝,那么按道理来说这个所谓的浅拷贝,修改了浅拷贝后的对象不会导致原来的对象改变吧?那为什么修改后会发生改变?//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
就是浅拷贝,那么为什么修改了objAfter
后objOrigin
也会变化?并且修改的还是对象当中的基本数据类型!(因为后面深拷贝还设计到对象当中的数据类型)
-
真正的浅拷贝
- 比如我们试图通过studen1实例,拷贝得到student2,如果是浅拷贝这种方式,大致模型可以示意成如下所示的样子:
- 很明显,值类型的字段会复制一份,而引用类型的字段拷贝的仅仅是引用地址,而该引用地址指向的实际对象空间其实只有一份。
- 所以浅拷贝是什么,就是赋值引用数据类型当中存储的基本数据类型,而引用数据类型当中的引用数据类型(比如数组当中嵌套对象的情况),仅仅只是将地址赋值给了另外一个对象!
深拷贝
- 深拷贝相较于上面所示的浅拷贝,除了值类型字段会复制一份,引用类型字段所指向的对象,会在内存中也创建一个副本,就像这个样子:
- 所以深拷贝是什么,就是不管对象当中嵌套了多少层引用数据类型还是基本数据类型,都建立一个新的给自己
一些常用的深浅拷贝方法
浅拷贝
展开运算符(扩展运算符)
- 可以看到,除了基本数据类型拷贝了,里面的引用数据类型并未进行拷贝,修改一个拷贝后的引用数据类型会影响原来的
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
- 可以看到,除了基本数据类型拷贝了,里面的引用数据类型并未进行拷贝,修改一个拷贝后的引用数据类型会影响原来的
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.parse
- 可以看到,除了基本数据类型拷贝了,里面的引用数据类型也进行了拷贝,修改一个拷贝后的引用数据类型不影响原来的
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
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
//比较是否相同 返回 false
console.log(deep[0] === objects[0]);