深浅拷贝
前言
为什么会出现深浅拷贝呢?
之所以会出现深浅拷贝的问题,实质上是由于JS对基本类型和引用类型的处理不同。基本类型指的是简单的数据段,而引用类型指的是一个对象,而JS不允许我们直接操作内存中的地址,也就是不能操作对象的内存空间,所以,我们对对象的操作都只是在操作它的引用而已。
在复制时也是一样,如果我们复制一个基本类型的值时,会创建一个新值,并把它保存在新的变量的位置上。而如果我们复制一个引用类型时,同样会把变量中的值复制一份放到新的变量空间里,但此时复制的东西并不是对象本身,而是指向该对象的指针。所以我们复制引用类型后,两个变量其实指向同一个对象,改变其中一个对象,会影响到另外一个。
var num = 10;
var obj = {
name: 'yanjie'
}
var num2 = num;
var obj2 = obj;
obj.name = 'haoshuai';
obj2.name; // 'haoshuai'
可以看到我们的obj和obj2都保存了一个指向该对象的指针,所有的操作都是对该引用的操作,所以对对象的修改会影响其他的复制对象。
浅拷贝
只考虑对象类型
如果我们要复制对象的所有属性都不是引用类型时,就可以使用浅拷贝,实现方式就是遍历并复制,最后返回新的对象
先上代码!!
function shallowCopy(obj) {
if (typeof obj !== 'object') return
let newObj = obj instanceof Array ? [] : {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key]
}
}
return newObj
}
- 我们先来看一下这句代码
let newObj = obj instanceof Array ? [] : {}
,先看一下obj instanceof Array
,这句代码的作用是用来检测Array.prototype
是否存在于参数object
的原型链上。然后后面接上了三元运算符?[]:{}
所以这句代码的意思是Arrqy.prototype是否存在于参数在object原型链上,如果存在,就把[]赋值给newObj,反之,把{}赋值给它。
- 既然讲到这里,那就把for in 和for of 也讲一下呗(主要是自己也不是太理解,嘿嘿!)
for … of是作为ES6新增的遍历方式,允许遍历一个含有iterator接口的数据结构并且返回各项的值,和ES3中的for … in的区别如下:
- for..in适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合.但是不能遍历对象,因为没有迭代器对象.与forEach()不同的是,它可以正确响应break、continue和return语句
- for-of循环不支持普通对象,但如果你想迭代一个对象的属性,你可以用for-in循环或对象的Object.keys()方法.
- hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
这么解释了一下,上面的代码就好看多了吧!
当if条件满足的时候,newObj[key]
就会复制 obj[key]
引用类型的指针,简单来讲就是当obj[key]的值改变时,newObj[key]的值也会改变,这就是浅拷贝。
深拷贝
先上代码!
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],b=deepClone(a);
a[0]=2;
console.log(a,b);
成功实现了!撒花
扩展
- JS中拷贝Array的slice和concat方法
slice拷贝
var a = [1,2,3];
var b = a.slice(); //slice
console.log(b === a);
a[0] = 4;
console.log(a);
console.log(b);
concat拷贝
var a = [1,2,3];
var b = a.concat(); //concat
console.log(b === a);
a[0] = 4;
console.log(a);
console.log(b);
看到结果,如果你觉得,这两个方法是深拷贝,那就恭喜你跳进了坑里!
来看看有意思的例子吧
var a = [[1,2,3],4,5];
var b = a.slice();
console.log(a === b);
a[0][0] = 6;
console.log(a);
console.log(b);
可以看到slice和contact对于第一层是深拷贝,但对于多层的时候,是复制的引用,所以是浅拷贝
最后的最后,再允许我多啰嗦一句呗:
JSON 对象的 parse 和 stringify都是深拷贝
总结
小伙伴们,这是手写题哦,所以一定要自己敲一下,那样印象才更深刻,加油哦!!!