# 深浅拷贝
# 前言
浅拷贝:复制原数据的基础类型的值,但遇到嵌套的引用类型时,由于直接赋值,拷贝后的对象某个 key 是引用类型时,双方用的是同一块堆内存,所以改变会互相影响。
深拷贝:与浅拷贝区分不同,则就是拷贝后的对象即使 key 值是引用类型,也是新开辟一块内存空间存储,所以双方的改变不会互相影响。
举个浅拷贝的例子:
const source = {
a: 1,
b: {
c: 1
}
};
// 用es6的解构实现浅拷贝
const target = { ...source };
target.b.c = 2;
console.log(source); // {a: b: {c: 2}} 会受target的改动影响。
# 浅拷贝实现
# es6 解构
前言中的例子就是用的 es6 解构实现浅拷贝,可以想象一下,其实就是遍历对象的 key 值,然后做复制操作。
我们主要聚焦一下,使用这种方式实现之后有什么现象,会有什么存在的问题。
- 不能复制原型链上的属性。
- 不能复制不可枚举
enumerable=false
的属性。(对象的属性描述符) - 可以复制
symbol
类型的属性。
let obj1 = { a: { b: 1 }, sym: Symbol(1) };
Object.defineProperty(obj1, 'innumerable', {
value: '不可枚举属性',
enumerable: false
});
let obj2 = { ...obj1 };
obj1.a.b = 2;
console.log('obj1', obj1);
console.log('obj2', obj2);
# Object.assign
与 es6 解构实现效果一致,一些不支持 es6 解构语法的场景下可以使用。
let obj1 = { a: { b: 1 }, sym: Symbol(1) };
Object.defineProperty(obj1, 'innumerable', {
value: '不可枚举属性',
enumerable: false
});
let obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
console.log('obj1', obj1);
console.log('obj2', obj2);
# 数组的两种浅拷贝方法
针对数组,也有两种特定的浅拷贝方式Array.proptotype.slice()
和Array.proptotype.concat()
。
const arr = [1, { a: 1 }, 3];
console.log(arr.slice());
console.log(arr.concat());
# 手动实现浅拷贝
遍历复制值,做一下数组和对象的区分即可。
const shallowClone = target => {
if (typeof target === 'object' && target !== null) {
const cloneTarget = Array.isArray(target) ? [] : {};
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = target[prop];
}
}
return cloneTarget;
} else {
return target;
}
};
# 深拷贝实现
# 乞丐版 JSON.stringify JSON.parse
const deepClone = target => {
try {
return JSON.parse(JSON.stringify(target));
} catch (err) {
return {};
}
};
这种方法是一种比较方便的浅拷贝方法,如果数据都是常见的字符串,数组,对象时,基本能满足日常需求。但是当值特殊时会存在一些问题:
拷贝的对象的值中如果有
函数
、undefined
、symbol
这几种类型,经过JSON.stringify
序列化之后的字符串中这个键值对会消失
;拷贝
Date
引用类型会变成字符串;无法拷贝
不可枚举
的属性;无法拷贝对象的
原型链
;拷贝
RegExp
引用类型会变成空对象;对象中含有
NaN
、Infinity
以及-Infinity
,JSON 序列化的结果会变成null
;无法拷贝对象的循环应用,即对象成环 (
obj[key] = obj
),会报错。
针对上述问题,下面代码是一个例子验证:
function Obj() {
this.func = function() {
alert(1);
};
this.obj = { a: 1 };
this.arr = [1, 2, 3];
this.und = undefined;
this.reg = /123/;
this.date = new Date(0);
this.NaN = NaN;
this.infinity = Infinity;
this.sym = Symbol(1);
}
let obj1 = new Obj();
Object.defineProperty(obj1, 'innumerable', {
enumerable: false,
value: 'innumerable'
});
console.log('obj1', obj1);
let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
console.log('obj2', obj2);
# 手动实现基础版
# 手动实现详细版
← 声明关键字 instanceof →