一、浅拷贝?
浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。
浅拷贝(只能拷贝一层):Object.assign和for in进行{ }和[ ]的拷贝。
//拷贝1层(测试)------------------------------------------------------------
//Object.assign()拷贝方法
//拷贝{}
a = {name:"张三"};
b = Object.assign({},a);
b.name = "李四"; //拷贝完之后进行改变属性,测试拷贝的属性值改变原数据是否改变。
console.log(a,b);
//输出:{name:"张三"}
{name:"李四"} (拷贝成功)
//拷贝[]
a = ["1","2","3"];
b = Object.assign([],a);
b[1]="hello";
console.log(a,b)
//输出:["1","2","3"]
["1","hello","3"] (拷贝成功)
//for in拷贝方法
var copy = function(a){
var res = a.constructor();
for(var key in a){
if(a.hasOwnProperty(key)){
res[key] = a[key]
}
}
return res;
}
a = {name:"123",people:{name:"456"}};
b = copy(a);
b.people.name="hello";
a = ["a","b","c"];b = copy(a);
b[1] = 0;//拷贝完之后进行改变属性,测试拷贝的属性值改变原数据是否改变。
console.log(a,b)
//输出:["a","b","c"]
["a","0","c"] (拷贝成功)
//拷贝2层(测试)-------------------------------------------------------------
//Object.assign()拷贝方法
a = {name:"abc",people:{name:"张三"}};
b = Object.assign({},a);
b.people.name="李四";
console.log(a,b)
//输出:{name:"abc",people:{name:"李四"}}
{name:"abc",people:{name:"李四"}} (拷贝失败) //for in拷贝方法
var copy = function(a){
var res = a.constructor();
console.log(res);
for(var key in a){
if(a.hasOwnProperty(key)){
res[key] = a[key]
}
}
return res;
}
a = ["a","b",{name:"张三"}];b = copy(a);b[2].name="李四";
console.log(a,b)
//输出:{name:"abc",people:{name:"李四"}}
{name:"abc",people:{name:"李四"}} (拷贝失败)
constructor( ) 是一种用于创建和初始化class创建的对象的特殊方法。
hasOwnProperty( ) 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键);
二、深拷贝
深拷贝:深拷贝复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。 深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。
深拷贝最简单的实现是:JSON.parse ( JSON.stringify ( obj ) ),同时有一定的缺陷:
① 对象的属性值是函数时,无法拷贝
② 原型链上的属性无法拷贝
③ 不能正确的处理Data类型的数据
④ 不能处理RegExp
⑤ 会忽略symbol、undefined
//JSON.parse(JSON.stringify(obj))方法拷贝2层
var deepCopy = function(a){
return JSON.parse(JSON.stringify(a));
}
var a = {name:"aaa",people:{name:"abc"}};
var b = deepCopy(a);
b.people.name = "def";
console.log(a,b)
//输出:{name:"aaa",people:{name:"abc"}}
{name:"aaa",people:{name:"def"}} (拷贝成功)//JSON.parse(JSON.stringify(obj))方法拷贝3层
var deepCopy = function(a){
return JSON.parse(JSON.stringify(a))
}
var a = [1,2,{name:"aaa"}];
var b = deepCopy(a);
b[2].name = "bbb";
console.log(a,b);
//输出:["1","2",{name:"aaa"}]
["1","2",{name:"bbb"}] (拷贝成功)
//JSON.parse(JSON.stringify(obj))拷贝函数的时候
var deepCopy = function(a){
return JSON.parse(JSON.stringify(a));
}
var a = {name:"aaa",fun:function(){console.log(1)},age:undefined};
var b = deep(a);
b.name = "bbb"
console.log(a,b);
//输出:{name:"aaa",fun:function(){console.log(1)},age:undefined};
{name:"bbb"} (拷贝失败,只拷贝到了name属性)
//JSON.parse(JSON.stringify(obj))拷贝原型链上的属性
function Person(name){
this.name=name;
}
var a = new Person("Bob");
var b = deep(a);
console.log(a.constructor == Person); //true
console.log(b.constructor == Object); //true
//先不说拷贝出的值,光是数据类型已经不同了 (拷贝失败)
console.log(a,b)
//输出:
// Person{name:"Bob"} {name:"Bob"}
注意:
上述方法会忽略值为function以及undefined的字段,而且对data类型的支持也不太友好。
上述方法只能克隆原始对象自身的值,不能克隆他继承的值。
深拷贝(完美拷贝):
① 如果是基本数据类型,直接返回;
② 如果是RegExp或者Data类型,返回对应类型;
③ 如果是复杂数据类型,递归;
④ 考虑循环引用的问题。
function deepClone(obj,hash = new WeakMap()){ //递归拷贝
if(obj instanceof RegExp) return new RegExp(obj);
if(obj instanceof Date) return new Date(obj);
if(obj === null || typeof obj !== 'object'){
//如果不是复杂数据类型,直接返回
return obj;
}
if(hash.has(obj)){
return hash.get(obj);
}
//如果obj是数组,那么 obj.constructor 是 [Function: Array]
//如果obj是对象,那么 obj.constructor 是 [Function: Object]
let t = new obj.constructor();
hash.set(obj,t);
for(let key in obj){
//递归
if(obj.hasOwnProperty(key)){ //是否是自身的属性
t[key] = deepClone(obj[key],hash);
}
}
return t;
}
var show = {
name:"Bob",
fun:function(){console.log(1)},
age:null,
pic:undefined,
}
var show2 = deepClone(show);
show2.name="Mary"
console.log(show,show2) //拷贝成功,完美拷贝
总结
深拷贝和浅拷贝是针对复杂数据类型来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝;
浅拷贝和深拷贝的区别:
浅拷贝只能复制对象或数组的第一层属性,而深拷贝是拷贝多层,每一级的数据都会被拷贝出来。