会创建一个一模一样的对象,新旧对象不共享内存,修改新对象不会影响原对象
。
只复制指向某个对象的指针,不复制对象本身,新旧对象还是共享同一块内存。修改新对象会影响原对象
。
在拷贝基本数据类型的时候其实是不区分深拷贝与浅拷贝的,因为都是拷贝原始数据类型的值。
当拷贝的是引用数据类型的时候,则区分浅拷贝、深拷贝,因为浅拷贝只复制引用数据类型的第一层属性,深拷贝可以对引用数据类型的属性进行递归复制。
js中的数据类型:
对基本数据类型进行复制,并重新赋值,并不会影响到原来的变量,这就是深拷贝。
let myname = '大华';
let age = myname;
age = 22;
console.log(myname); //大华
console.log(age); //22
myname
这个变量会在内存中开辟一个地址,存储一个值为大华
,age
这个变量复制了myname
所以他们指向的都是同一个地址,同时也指向同一个值,但对 age
重新赋值时,会在内存在开辟一个地址来存储另一个值22
。
对引用数据类型进行复制,并重新赋值,会影响到原来的变量,这就是浅拷贝。
let obj1 = {
myname: "大华",
age: 12
}
let obj2 = obj1;
obj2.age = 22;
//里面的age都变成22
console.log(obj1);
console.log(obj2);
对于引用数据类型来说,就有了堆
和栈
的说法,obj1
会开辟一个内存地址,与值相对应(这就是栈),但这个内存地址的值又会被分配到另一个地址,在另一个地址中又创建了对应的值(也就是堆),obj2
在进行复制时,就会通过栈一路找到堆进行复制,一旦修改,那么obj1
和obj2
都会受影响,因为它们指向的都是同一个地址。
实际的项目开发过程中,在多数情况下不希望将对象进行浅拷贝,因为值会相互影响,容易出错,可以把浅拷贝转换成深拷贝。
1、JSON.stringify()
JSON.parse(JSON.stringify(obj)) 是目前比较常用的深拷贝方法之一,它的原理就是利用 JSON.stringify 将 js 对象序列化(JSON字符串),再使用 JSON.parse 来反序列化(还原) js 对象。
这个方法可以简单粗暴的实现深拷贝,但是还存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过 JSON.stringify() 进行处理之后,都会消失。
let obj1 = { a: 0,b: {c: 0}};
let obj2 = JSON.parse(JSON.stringify(obj1));
obj1.a = 1;
obj1.b.c = 1;
console.log(obj1); // {a: 1, b: {c: 1}}
console.log(obj2); // {a: 0, b: {c: 0}}
2、函数库lodash的_.cloneDeep方法
该函数库也有提供_.cloneDeep用来做 Deep Copy
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
3、扩展运算符
使用扩展运算符可以在对对象或数组的第一层的拷贝是深拷贝。
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}}
4、实现深拷贝函数(解决循环引用)
// 深拷贝的实现
function deepCopy(obj, map) {
//判断是否是第一次调用deepCopy方法,是的话创建一个weakmap实例来装遍历过程中出现过的对象
if(!map){
map = new WeakMap()
}
//判断传入的obj是否为对象
if( obj === null || typeof obj !== 'Object' ){
return obj
}
//如果map中已经存在这个对象说明出现了循环引用问题
if(map.get(obj)){
return obj
}
//map中没有就往map中存入该对象
map.set(obj,obj)
//根据obj的类型来给newObj创建初始值
let newObj = Array.isArray(obj) ? [] : {}
//遍历obj
for(let i in obj){
if(obj.hasOwnproperty(i)){ //判断当前属性是否为obj自身的属性
if(typeof obj[i] === 'Object'){ //判断当前属性是否为对象类型
newObj[i] = deepCopy(obj[i],map) //如果是对象类型就使用该方法进行递归处理
}else{
newObj[i] = obj[i] //不是对象类型就直接拷贝
}
}
}
return newObj //返回拷贝完成的newObj
}
1、Object.assign()
Object.assign() 是 ES6 中对象的拷贝方法,接受的第一个参数是目标对象,其余参数是源对象,用法:Object.assign(target, source_1, ···),该方法可以实现浅拷贝,也可以实现一维对象的深拷贝。
该方法需要注意的是:
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、扩展运算符
使用扩展运算符可以在对对象或数组的第二层开始的拷贝是浅拷贝。
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、数组方法实现数组浅拷贝
(1)Array.prototype.slice
slice() 方法是 JavaScript 数组的一个方法,这个方法可以从已有数组中返回选定的元素:用法:array.slice(start, end),该方法不会改变原始数组。
该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
let arr = [1,2,3,4];
console.log(arr.slice()); // [1,2,3,4]
console.log(arr.slice() === arr); //false
(2)Array.prototype.concat
concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
let arr = [1,2,3,4];
console.log(arr.concat()); // [1,2,3,4]
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;
}
更多内容链接☛ 将浅拷贝转换成深拷贝,Vue中的浅拷贝与深拷贝