浅拷贝:只是增加了一个指针,指向已经存在的内存地址。
仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅拷贝出来的对象也会相应的改变。
简单来说,就是改变其中一个对象,另一个对象也会跟着改变。
深拷贝:
是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。
在计算机中开辟一块新的内存地址用于存放赋值对象。
简单的讲,就是复制出来的每个对象都有自己的内存空间,不会相互干扰。
深拷贝的三种实现方式:
1.常用JSON.parse(JSON.stringify(obj));====>先序列化,再反序列化。
缺点(bug)是: 会忽略undefined、symbol、funciton
2.jQuery中$.extend([deep],target,...object);
deep---表示深拷贝,Boolean类型;
target---目标对象;
...object---需要进行合并对象
3.利用原生js进行深拷贝-手写实现深拷贝
(1)for循环遍历所有属性(只拷贝当前对象的属性)----hasOwnProperty():返回布尔值。
(2)判断是引用数据类型,还是基本数据类型
①如果是基本数据类型:则使用浅拷贝拷贝;
②如果是引用数据类型,(object)
a.再判断是否为数组,如果为数组,则为target[key]赋值为[](数组),否则为{}对象。
b.接着再进行深拷贝(递归深拷贝)
function deepCopy(source,target){
for(var key in source) {
if(source.hasOwnProperty(key)){ // 只拷贝当前对象的属性
// 判断是否为引用数据类型
if(typeof source[key] == 'object'){
// 引用数据类型,赋值为数组[]或者对象{},递归进行深拷贝。
target[key] = Array.isArray(source[key])?[]:{};
deepCopy(source[key],target[key]);
} else {
// 基本数据类型---浅拷贝
target[key] = source[key];
}
}
}
}
浅拷贝的实现方式:
(1)Object.assign()
Object.assign()是es6中对象的拷贝方法,接受的第一个参数是目标对象,其余参数是源对象,用法:Object.assign(target,source_1,...),该方法可以实现浅拷贝,也可以实现一维对象的深拷贝。
同样Object.assign可以做对象的合并
用法:将第2个参数开始的对象合并到第一个对象中
let newObj = Object.assign(...obj1,...obj2)//---如果是这样写,会改变原对象的数据
// 为了不破坏原对象,所以写入一个空对象{}
let newObj = Object.assign({},...obj1,...obj2)
eg:
let target = {a:1};
let object2 = {b:2};
let object3 = {c:3};
Object.assign(target,object2,object3);
console.log(target);// {a:1,b:2,c:3}
(2)es6的扩展运算符
使用扩展运算符可以在构造字面量对象的时候,进行属性的拷贝。
语法:let cloneObj = {..obj};
eg:
let obj1 = {a:1,b:{c:1}};
let obj2 = {...obj1};
obj1.a = 2;
console.log(obj1);// {a:2,b:{c:1}}
console.log(obj2);// {a:1,b:{c:1}}
obj1.b.c = 2;
console.log(obj1);// {a:2,b:{c:2}}
console.log(obj2);// {a:1,b:{c:2}}
(3)数组方法实现数组浅拷贝
a.Array.prototype.slice()
slice()方法是JavaScript数组的一个方法,这个方法可以从已有数组中返回选定的元素:
用法:array.slice(start,end),该方法不会改变原始数组。
该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
eg:
let arr = [1,2,3,4]
console.log(arr);// [1,2,3,4]
console.log(arr.slice());// [1,2,3,4]
console.log(arr.slice()==arr);// false
console.log(arr.slice()===arr);// false
start:①提取起始处的索引(从0开始)
②如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2)表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)
eg:arr.slice(-2)//[3,4]
③如果省略start,则slice从索引0开始。
④如果start超出原数组的索引范围,则会返回空数组。
end:①提取终止处的索引(从0开始)。
slice会提取原数组中索引从start到end的所有元素(包括start,但不包括end)
②如果该参数为负数,则它表示在原数组的倒数第几个元素结束抽取。
eg:slice(-2,-1)//[3]→(抽取了原数组中的倒数第二个元素到最后一个元素但不包含最后一个元素)
③如果end被省略,则slice会一直提取到原数组末尾。
④如果end大于数组的长度,slice也会一直提取到原数组末尾。
b.Array.prototype.concat()
concat()方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
let arr = [1,2,3,4];
console.log(arr);// [1,2,3,4]
console.log(arr.concat());// [1,2,3,4]
console.log(arr.concat()==arr);// false
console.log(arr.concat()===arr);// false
(4)手写实现浅拷贝
function shallowCopy(object) {
// 只拷贝对象
if(!object || typeof object !== 'object') return;
// 根据object的类型判断是新建一个数组还是对象
let newObject = Array.isArray(object)?[]:{};
// 遍历object,并且判断是object的属性才拷贝
for(let key in object) {
if(object.hasOwnProperty(key)){
newObject[key] = object[key];
}
}
return newObject;
}
网友评论