一、前置知识
js中的对象分为基本类型和复合(引用)类型,前者存放在栈内存,后者存放在堆内存。
栈内存存放一些基本类型的变量和对象的引用变量,堆内存用于存放由new创建的对象。
JS——数据类型及判断.
二、浅拷贝
对对象地址的复制,不会进行递归复制,并没有开辟新的栈,也就是复制的结果是两个对象指向同一个地址。
var arr1 = [1,2,3,4,5];
var arr2 = arr1;
arr1.push(6);
console.log(arr1); // [1,2,3,4,5,6]
console.log(arr2); // [1,2,3,4,5,6]
一般直接赋值=只适用于基本类型变量的赋值,而复合类型的变量直接复制只是复制了内存地址,仅是浅拷贝。
三、深拷贝
将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。并开辟了一块新的内存地址来存放复制的对象。
所以深拷贝时,无论是对a操作还是对b操作,都是只改变自己的内容。
以下是一些比较常用的深拷贝方法。
1.JSON.stringify/parse的方法
let arr1 = [1,2,3,4];
let arr2 = JSON.parse(JSON.stringify(arr1));
arr2.push(5);
console.log(arr1); //[1, 2, 3, 4]
console.log(arr2); //[1, 2, 3, 4, 5]
const obj1 = {a:'a',b:'b'};
const obj2 = JSON.parse(JSON.stringify(obj1));
obj2.a = 'aa';
console.log(obj1); // {a:'a',b:'b'};
console.log(obj2); // {a:'aa',b:'b'};
但是,此方法有个bug就是无法对对象中的function或undefined进行拷贝。
const obj = {
name:'zhangsan',
fn:function(){
console.log('Hello World');
}
}
console.log(obj); // {name: "zhangsan", fn: ƒ}
const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // {name: "zhangsan"}
obj.fn(); // Hello World
obj2.fn(); // 报错,obj2.fn()不是一个函数
JSON.stringify/parse实现深拷贝的时候,需要求目标对象(非 undefined,function)
2.使用递归方法
使用递归方法,就是对每一层的数据都实现一次 创建对象->对象赋值的操作
function deepClone(item){
const target = item.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for(let keys in item){ // 遍历目标
if(item.hasOwnProperty(keys)){ // hasOwnProperty用来排除原型链上的属性,而不是定义在对象自身上的属性
if(item[keys] && typeof item[keys] === 'object'){ // 如果值是对象,就递归一下。typeof 为对象,则有可能是array或者是object
target[keys] = item[keys].constructor === Array ? [] : {};
target[keys] = deepClone(item[keys]);
}else{ // 如果不是,就直接赋值
target[keys] = item[keys];
}
}
}
return target;
}
const obj1 = {a:'a',b:'b'};
const obj2 = deepClone(obj1);
obj2.a = 'aa';
console.log(obj1); // {a:'a',b:'b'};
console.log(obj2); // {a:'aa',b:'b'};
const obj3 = {
name:'zhangsan',
fn:function(){
console.log('Hello World');
}
}
console.log(obj3); // {name: "zhangsan", fn: ƒ}
const obj4 = deepClone(obj3);
console.log(obj4); // {name: "zhangsan", fn: ƒ}
hasOwnProperty函数
使用递归方法来深拷贝可以深拷贝数组、对象、以及带函数的对象。
3.使用es6的Array.from(针对数组)
var arr1 = [1,2,3];
var arr2 = Array.from(arr1);
arr1.push(4);
console.log(arr1); //[1,2,3,4]
console.log(arr2); //[1,2,3]
arr2.push(5);
console.log(arr1); //[1,2,3,4]
console.log(arr2); //[1,2,3,5]
4.使用es6的…
var arr1=[1,2,3];
var arr2=[...arr1];
arr1.push(4);
console.log(arr1); //[1,2,3,4]
console.log(arr2); //[1,2,3]
arr2.push(5);
console.log(arr1); //[1,2,3,4]
console.log(arr2); //[1,2,3,5]
四、总结
- 在对象中赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值
- 递归是做复杂深拷贝比较合理的方法
- JSON深拷贝只能是对象中没有function时可以使用