赋值,浅拷贝,深拷贝三者区别
赋值
直接赋值对象的地址,因此两个对象指向同一个内存地址,会相互影响
var a = [1, 2, 3, 4];
var b = a;
b.push(5);
console.log(b); // 1,2,3,4,5
console.log(a); // 1,2,3,4,5
拷贝
创建一个新对象,这个对象有着原始对象属性值的一份拷贝,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是引用的内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象(它只能拷贝一层对象。如果有对象的嵌套,那么浅拷贝将无能为力)
特点:新对象属性改变,不会影响基本类型的属性,但会影响引用类型的属性
深拷贝
将一个对象从内存中完整的拷贝一份出来,存放在一个新对象中
特点:对象指向不同地址,不会相互影响
图解---借助ConardLi大佬
深浅拷贝方法
浅拷贝方法
Object.assign()
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
let obj1 = { person: { name: 'yyqx', age: 20 }, job: 'Star' };
let obj2 = Object.assign({}, obj1);
obj2.name = 'lyj';
obj2.job = 'actor'
console.log(obj1);
展开运算符...
与Object.assign()的功能相同
let obj1 = { name: 'yyqx', message: { job: 'Star', age: 20 } };
let obj2 = { ...obj1 };
obj1.name = 'lyj';
obj1.message.job = 'actor'
console.log(obj2);
Array.prototype.concat()
let arr1 = [11, 28, { name: 'yyqx' }];
let arr2 = arr1.concat();
arr2[2].name = 'lyj';
console.log(arr1);
Array.prototype.slice()
let arr1 = [11, 28, { name: 'yyqx' }];
let arr3 = arr1.slice();
arr3[2].name = 'lyj';
console.log(arr1);
函数库lodash的_.clone方法
var _ = require('lodash');
var obj = {
name: 'yyqx',
message: { job: 'Star' },
color: ['red', 'yellow', 'blue']
}
var obj1=_.clone(obj);
console.log(obj1.message.job===obj1.message.job); // true
深拷贝方法
JSON.parse(JSON.stringify())
这也是利用JSON.stringify将对象转成JSON字符串,再用JSON.parse把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。
let arr=[11,28,{name:'yyqx'}];
let arr4=JSON.parse(JSON.stringify(arr));
arr4[2].name='lyj';
console.log(arr,arr4);
这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。
let arr = [11, 28, { name: 'yyqx' }, function () { }];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].name = 'lyj';
console.log(arr, arr4);
JQuery.extend()方法
$.extend(deepCopy, target, object1, [objectN])---第一个参数为true,就是深拷贝
var $ = require('jquery');
var obj = {
name: 'yyqx',
message: { job: 'Star' },
color: ['red', 'pink', 'blue']
}
var obj1 = $.extend(true, {}, obj);
console.log(obj.message.job === obj1.message.obj); // false
函数库loadsh的_.cloneDeep()方法
var _ = require('loadsh');
var obj = {
name: 'yyqx',
message: { job: 'Star' },
color: ['red', 'pink', 'blue']
}
var obj1 = _.cloneDeep(obj);
console.log(obj.message.job === obj1.message.obj); // fasle
手写递归方法
递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。
详细版本---如何写出一个惊讶面试官的深拷贝
function deepClone(obj, hash = new WeakMap()) {
// 如果是null或undefined,就不需要进行拷贝
if (obj === null || obj === undefined) return obj;
// 如果是内置对象,就需要返回新内置对象
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 如果是函数或者基本数据类型,不需要深拷贝
if (typeof obj !== 'object') return obj;
// 是对象的话就要进行深拷贝
if (hash.get(obj)) return hash.get(obj);
// obj是对象Object的实例对象,obj中的__proto__指向Object.prototype,
// 而Object.prototype中的constructor指向Object本身
// 所以相当于新创建了一个对象的实例
let cloneObj = new obj.constructor();
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
- 实验
let obj = { name: 'yyqx', add: { x: 100 } };
obj.o = obj; // 对象存在循环引用的情况
let obj1 = deepClone(obj);
obj.add.x = 200;
obj1.name = 'lyj'
console.log(obj,obj1);
总结
仅为学习笔记!!!
- 参考文章 浅拷贝与深拷贝