前言:
在面试中,你必须要知道的一个知识点,那就是浅拷贝和深拷贝,那么就必须知道基本数据类型和引用类型,其实深拷贝和浅拷贝的主要区别就是其在内存中的存储类型不同。
网片来自网络(侵删)
重点:你需要记住一句话:值类型传递的是值,引用类型传递的是地址
基本数据类型
这里就不做过多介绍了,基本数据类型值放在栈区,可直接访问与修改,且相互之间不会影响
引用类型
引用类型地址放在栈区,值放在堆区,所以当你进行赋值操作,其实赋值的是地址,所以二者之间是有关系的:
var obj1={name:'mengyun',age:18
};
var obj2 = obj1;
obj2.name = 'meimei';
console.log(obj1);//{name:'meimei',age:18}
console.log(obj2);//{name:'meimei',age:18}
可见是有联系的,而且曾经我一度以为赋值操作就是浅拷贝,其实不是的,还是太年轻~
下面看一个例子:
var obj1 = {name: 'mengyun',age: 18,hobby:{ play: [ 'footboll' ], read: 'book' },eat: ['苹果', '香蕉', '菠萝']
};var obj2 = obj1;function shallowCopy(target) {var result;if (Object.prototype.toString.call(target).slice(8, -1) === 'Object'){result={}}else if(Object.prototype.toString.call(target).slice(8, -1) === 'Array'){result=[]}for (var prop in target) {if (target.hasOwnProperty(prop)) {result[prop] = target[prop];}}return result;
}
var obj3 = shallowCopy(obj1);
obj2.name = "meimei";
obj3.age = "20";
obj2.hobby.play = ["games"];
obj3.hobby.read= ["news"];
obj3.eat = ['哈密瓜']; // 修改一层引用类型
console.log(obj1);
console.log(obj2);
console.log(obj3);
结果如图:
因为浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。所以就会出现改变浅拷贝得到的 obj3 中的引用类型时,会使原始数据得到改变。而且浅拷贝可以修改第一层的引用类型
浅拷贝:
拷贝就是复制,就相当于把一个对象中的所有的内容,复制一份给另一个对象,直接复制,或者说,就是把一个对象的地址给了另一个对象,他们指向相同,两个对象之间有共同的属性或者方法,都可以使用
实现浅拷贝的方法:
- 手写一个浅拷贝
function shallowCopy(target) {var result;if (Object.prototype.toString.call(target).slice(8, -1) === 'Object'){result={}}else if(Object.prototype.toString.call(target).slice(8, -1) === 'Array'){result=[]}for (var prop in target) {if (target.hasOwnProperty(prop)) {result[prop] = target[prop];}}return result;
}
- Object.assign()
var obj1 = {
name: 'mengyun',age: 18,hobby: { play: ['footboll'], read: 'book' },eat: ['苹果', '香蕉', '菠萝'],
};var obj2 = obj1;var obj3 = Object.assign({}, obj1);
obj2.name = "meimei";
obj3.age = "20";
obj2.hobby.play = ["games"];
obj3.hobby.read = ["news"];
obj3.eat = ['哈密瓜']; // 修改一层引用类型
console.log(obj1);
console.log(obj2);
console.log(obj3);
结果和上面一样
- 数组的浅拷贝
展开运算符
var arr1=[1,2,3];
var arr2 = arr1;
arr2[1]='666';
console.log(arr1);//[1, "666", 3]
console.log(arr2);//[1, "666", 3]var arr3 = [5,6,7];
var arr4 = [...arr3];
arr4[1]='666';
console.log(arr3);//[5,6,7]
console.log(arr4);//[5, "666", 7]
concat():
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
深拷贝:
把一个对象中所有的属性或者方法,一个一个的找到,并且在另一个对象中开辟相应的空间,一个一个的存储到另一个对象中
实现深拷贝的方法:
- JSON.parse(JSON.stringify())
let obj1 = {a:3,b:4
}let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
obj2.a = 5;
console.log(obj1); //{a: 3, b: 4}
console.log(obj2); //{a: 5, b: 4}
- 手写一个深拷贝(递归)
法1:
function deepClone(target) {var result;if (Object.prototype.toString.call(target).slice(8, -1) === 'Object') {result = {}} else if (Object.prototype.toString.call(target).slice(8, -1) === 'Array') {result = []}else{return target}for (var prop in target) {if (target.hasOwnProperty(prop)) {result[prop] = deepClone(target[prop]);}}return result;
}
法2:
function deepClone(target) {if(typeof target !=='object' || target==null){//target是null,或者不是数组、对象,直接返回return target}let result;//初始化结果if(target instanceof Array){result = []}else{result = {}}for (var prop in target) {if(target.hasOwnProperty(prop)){//保证key不是原型上的属性result[prop] = deepClone(target[prop]);}}return result;
}
测试案例:
var obj1 = {name: 'mengyun',age: 18,hobby: { play: ['footboll'], read: 'book' },eat: ['苹果', '香蕉', '菠萝'],
};var obj2 = obj1;var obj3 = deepClone(obj1);
obj2.name = "meimei";
obj3.age = "20";
obj2.hobby.play = ["games"];
obj3.hobby.read = ["news"];
obj3.eat = ['哈密瓜']; // 修改一层引用类型
console.log(obj1);
console.log(obj2);
console.log(obj3);
结果:
最后:
记住这张图
(图片侵删)
区别:
浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制