问题:什么是深浅拷贝?
JS的拷贝(copy),之所以分为深浅两种形式,是因为JS变量的类型存在 基本类型与reference(引用)两种区别。当然,大多数编程语言都存在这种特性。为啥分深浅拷贝-依据变量的类型
什么是拷贝,从字面意思就是复制 ,复制出一个副本
浅拷贝只是简单的复制,对对象里面的对象属性和数组属性只是复制相同的地址
如果对象属性是基本的数据类型,复制的就是基本类型的值给新对象;但如果属性是引用数据类型,复制的就是内存中的地址,新对象的引用类型数据的修改会改变原对象
而深拷贝,在堆内存中重新开辟一个存储空间,完全克隆一个一模一样的对象;
深拷贝**原理:**将原对象从内存中完整地拷贝出来一份给新对象,并从堆内存中开辟一个全新的空间存放新对象,且新对象的修改并不会改变原对象,二者实现真正的分离。
浅拷贝方式:有两种方式,
- 一种是把一个对象里面的所有的属性值和方法都一个个复制给另一个对象,浅拷贝只是简单的复制,对对象里面的对象属性和数组属性只是复制了地址,
- 另一种是直接把一个对象赋给另一个对象,使得两个都指向同一个对象。
浅拷贝和深拷贝举列
拷贝对象为基本数据类型,永远都是深拷贝。 拷贝对象中属性值有引用数据类型,可分为深拷贝和浅拷贝
拷贝对象为基本数据类型
js中的基本数据类型:String Number Boolean Null Undefined,在赋值的过程中永远都是深拷贝。
例如,let a = 10 ; b = a , 修改其中一个变量的值,不会影响到另一个变量的值
再简单的例子,也要试一下
var a=1;
b=a;
b=2223;
console.log("a:"+a);
console.log("b:"+b);
//结果 a:1 b:2223
浅拷贝
拷贝对象中有引用数据类型
引用类型:Object、Array、Date、Function等;引用类型的值是对象,保存在堆内存中。
object.assign 实现浅拷贝
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回****目标对象
- 可枚举可以理解为是否可以被遍历被列举出来,可枚举性决定了这个属性能否被for…in查找遍历到。
object.assign 是 ES6中 object 的一个方法,该方法可以用于JS 对象的合并等多个用途,其中一个用途就是可以进行浅拷贝。
object.assign 的语法为:Object.assign(target, …sources)
该方法的第一个参数是 目标对象,后面的参数全是源对象(也可以是多个来源)。
let newrowdata=Object.assign({},line_data)
如果拷贝的属性在来源对象和目标对象中都有,会被最后一个来源对象覆盖
var newrowdata=Object.assign({b:2,a:3},{a:1})
newrowdata:{b: 2, a: 1}
var newrowdata=Object.assign({b:2,a:3},{a:1},{a:222})
newrowdata:{b: 2, a: 222}
Object.assign拷贝,返回的结果是目标对象
改变目标对象 中基本类型的值,不会影响源对象中的,如果属性值是引用类型的,则会改变源对象中的
var obj1={name: "zhangsan", age: 20},
obj2={group: 3, id: 2,arr:["apple","orange"]};
var obj=Object.assign(obj1,obj2) //{b: 2, a: 1}
obj.name='Alice';
//测试返回值和目标对象的关系
console.log(obj); //{name: 'Alice', age: 20, group: 3, id: 2, arr: ['apple', 'orange']}
console.log(obj1);
console.log(obj==obj1); //true 说明拷贝时返回的是目标对象 Object.assign 第一个参数
console.log(obj2);// {group: 3, id: 2,arr:["apple","orange"]}
//测试改变目标对象属性值,对源对象的影响
obj1.group=77;
console.log(obj2.group);//3
obj2.group=345
obj1.arr[2]="tomato"
console.log(obj2.arr);//['apple', 'orange', 'tomato']
concat 拷贝数组
concat用于连接两个或者多个数组,该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
数组的 concat 方法其实也是浅拷贝,当连接一个含有引用类型的数组时,需要注意修改原数组中的元素的属性,因为它会影响拷贝之后连接的数组
语法
array1.concat(array2, array3,…, arrayX)
array2,array3…,arrayX 不必传。需要需要连接的数组
const arr = [1, 2, 3,{a:1}];
// const newArr = arr.concat();
const newArr = arr.concat();
newArr[1] = 0;
console.dir(arr); //[1,2,3,{a:1}]
console.dir(newArr);//[1,0,3,{a:1}]
//拷贝时,如果属性是基本类型,直接拷贝值,改变一个不会影响副本
newArr[3].a = 4;
console.log(arr); //[1,2,3,{a:4}];
console.log(newArr); // [ 1, 0, 3, {a: 4} ]
// 如果属性是引用类型的,改变了值,会影响副本的
slice 拷贝数组
扩展运算符方式
深拷贝
JSON.stringify
JSON.stringify() 方法用于将 JavaScript 值(通常为对象或数组)转换为 JSON 字符串。
JSON.parse() 方法用于将一个 JSON 字符串转换为对象。
const obj1 = { a: 1, b: [1, 2, 3] }
const str = JSON.stringify(obj1);
const obj2 = JSON.parse(str);
console.log(obj2); //{a:1,b:[1,2,3]}
obj2.a = 2;
obj2.b.push(4);
console.log(obj1); //{a:1,b:[1,2,3]}
console.log(obj2);//{a:2,b:[1,2,3,4]}
可以看到通过改变 obj2的 b 属性, obj1 这个对象也不受影响。