深浅拷贝

Object.assign

从一个或多个源对象分配到目标对象,返回目标对象

1
2
3
4
5
6
7
8
9
10
11
12
let obj1 = { a: { b: 1 }, sym: Symbol(1) };
Object.defineProperty(obj1, 'innumerable', {
value: '不可枚举属性',
enumerable: false,
});
let obj2 = {};
Object.assign(obj2, obj1);

obj1.a.b = 2;

console.log({ obj1 }); // { a: { b: 2 }, sym: Symbol(1), innumerable: "不可枚举属性" }
console.log({ obj2 }); // { a: { b: 2 }, sym: Symbol(1) }
  • 不会拷贝不可枚举属性
  • 可以拷贝 symbol 类型的属性
  • 他不会拷贝对象的继承属性

… 扩展运算符

Object.assign拥有同样的缺陷,但是拷贝基本类型的值的时候会更加方便

concat

用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组

slice

浅拷贝数组,仅仅针对数组类型。
类数组可以使用 Object.prototype.slice.call(arr),表示从 Array 中获取 slice 方法,绑定到 arr 中,并执行 slice。

测试:
array1cloneArray1第一项的 name 属性都变为’wangwu’

1
2
3
4
5
6
7
8
9
10
11
const array1 = [
{ id: 1, name: 'zhangsan' },
{ id: 2, name: 'lisi' },
];
// const cloneArray1 = [...array1]
// const cloneArray1 = Object.assign(array1)
// const cloneArray1 = array1.concat([])
const cloneArray1 = array1.slice();
cloneArray1[0].name = 'wangwu';
console.log('array1', array1);
console.log('cloneArray1', cloneArray1);

浅拷贝的实现

1
2
3
4
5
6
7
8
9
10
11
function shallowClone(obj) {
if (typeof obj !== 'object' || target === null) return;
const newObj = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
// 忽略原型上继承过来的属性
newObj[key] = obj[key];
}
}
return newObj;
}

深拷贝的实现

乞丐版:JSON.stringify

将对象序列化为 JSON 字符串,对对象里面的内容转为字符串,再用 JSON.parse 生成新对象。
注意点:

  • 如果对象”值”中有函数、undefined、symbol 这几种类型,经过序列化之后这个键值对会消失
  • 拷贝 Date 引用各类型会变成字符串
  • 拷贝 RegExp 引用类型会变成空对象
  • 无法拷贝不可枚举属性
  • 无法拷贝对象的原型链
  • 无法拷贝对象的循环应用,即对象成环(obj[key] = obj)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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);
this[Symbol(2)] = 'sym2';
}

let obj1 = new Obj();

Object.defineProperty(obj1, 'innumerable', {
enumerable: false,
value: 'innumerable',
});
console.log('obj1', obj1);
let obj22 = JSON.parse(JSON.stringify(obj1));
console.log('obj22', obj22);

pic.1708326973863

基础版

注意点:

  • 不能复制不可枚举和 Symbol 类型的属性
  • 对于 Date、RegExp 这样的引用类型不能正确的拷贝
  • 没解决循环引用问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function deepClone(target) {
if (typeof target === 'object' && target !== null) {
const newObj = Array.isArray(target) ? [] : {};
for (const key in target) {
if (typeof target[key] === 'object') {
newObj[key] = deepClone(target[key]);
} else {
newObj[key] = target[key];
}
}
return newObj;
}
}

const obj3 = deepClone(obj1);
console.log('obj3', obj3);

pic.1708326963610

改进版

  • 对于不可枚举和 Symbol 类型,可以 Reflect.ownKeys(),Reflect.ownKeys === Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(Obj))
  • 对于 Date 和 RegExp 类型,直接返回一个实例对象
  • Object.getOwnPropertyDescriptors 可以获取所有自身属性的描述符,以及对应的特性,顺便结合 Object 的 create 方法创建一个新对象,并集成原对象的原型链
  • 利用 WeakMap 类型作为 hash 表,因为 WeakMap 是弱引用类型,可以有效防止内存泄漏,如果存在循环引用,就直接返回 WeakMap 存储的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const isComplexDataType = (obj) =>
(typeof obj === 'object' || typeof obj === 'function') && obj !== null;

function deepCloneImprove(obj, hash = new WeakMap()) {
if (obj.constructor === Date) return new Date(obj); // 日期对象直接返回一个新的日期对象
if (obj.constructor === RegExp) return new RegExp(obj); //正则对象直接返回一个新的正则对象
//如果循环引用了就用 weakMap 来解决
if (hash.has(obj)) return hash.get(obj);
let allDesc = Object.getOwnPropertyDescriptors(obj);
//遍历传入参数所有键的特性
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc);
//继承原型链
hash.set(obj, cloneObj);

for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] =
isComplexDataType(obj[key]) && typeof obj[key] !== 'function'
? deepCloneImprove(obj[key], hash)
: obj[key];
}
return cloneObj;
}

验证代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let obj4 = {
num: 0,
str: '',
boolean: true,
unf: undefined,
nul: null,
obj: { name: '我是一个对象', id: 1 },
arr: [0, 1, 2],
func: function () {
console.log('我是一个函数');
},
date: new Date(0),
reg: new RegExp('/我是一个正则/ig'),
[Symbol('1')]: 1,
};
Object.defineProperty(obj4, 'innumerable', {
enumerable: false,
value: '不可枚举属性',
});
obj4 = Object.create(obj4, Object.getOwnPropertyDescriptors(obj4));
obj4.loop = obj4; // 设置loop成循环引用的属性
let cloneObj = deepCloneImprove(obj4);
cloneObj.arr.push(4);
console.log('obj4', obj4);
console.log('cloneObj', cloneObj);

pic.1708326948006

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×