看这个文章之前,看一下这个作者的文档,讲了一下堆和栈、基本数据和引用数据,比较基础的一些东西:https://www.cnblogs.com/echolun/p/7889848.html
如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力。
浅拷贝:复制对象的时候,只对第一层键值对进行独立的复制
简单的实现如下 es5
// 浅拷贝实现
function shadowCopy(target, source){
if( !source || typeof source !== 'object'){
return;
}
// 这个方法有点小trick,target一定得事先定义好,不然就不能改变实参了。
// 具体原因解释可以看参考资料中 JS是值传递还是引用传递
if( !target || typeof target !== 'object'){
return;
}
// 这边最好区别一下对象和数组的复制
for(var key in source){
if(source.hasOwnProperty(key)){
target[key] = source[key];
}
}
}
es6
注意,在属性拷贝过程中可能会产生异常,比如目标对象的某个只读属性和源对象的某个属性同名,这时该方法会抛出一个 TypeError 异常,拷贝过程中断,已经拷贝成功的属性不会受到影响,还未拷贝的属性将不会再被拷贝。
注意, Object.assign 会跳过那些值为 null 或 undefined 的源对象。
Object.assign(target, ...sources)
var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
深拷贝
递归属性遍历
es5
一般来说,在JavaScript中考虑复合类型的深层复制的时候,往往就是指对于Date、Object与Array这三个复合类型的处理。我们能想到的最常用的方法就是先创建一个空的新对象,然后递归遍历旧对象,直到发现基础类型的子节点才赋予到新对象对应的位置。不过这种方法会存在一个问题,就是JavaScript中存在着神奇的原型机制,并且这个原型会在遍历的时候出现,然后原型不应该被赋予给新对象。那么在遍历的过程中,我们应该考虑使用hasOenProperty方法来过滤掉那些继承自原型链上的属性:
function clone(obj) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
利用JSON深拷贝
对于一般的需求是可以满足的,但是它有缺点。下例中,可以看到JSON复制会忽略掉值为undefined以及函数表达式。
JSON.parse(JSON.stringify(obj));