2018-04-08 | Javascript | UNLOCK

克隆

克隆的实现

浅克隆

浅克隆,只是最外面的部分被克隆,深层的对象依然是引用指向被克隆对象的同一块堆内存,所以存在的问题就是,当被克隆的对象或者新对象发生修改,两个对象会同时被修改。新的api Object.assign()也有同样的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const oldObj = { //=> 原对象
a: 1,
b: ['d','e'],
c: { f: {g: 2} }
};

function shallowClone(o){ //=> 浅克隆函数
const obj = {};
for(let i in o){
obj[i] = o[i];
}
return obj;
};

let newObj = shallowClone(oldObj); //=> 克隆的新对象
console.log(oldObj === newObj); //=> false
console.log(oldObj.c.f === newObj.c.f); //=> true

newObj.a = 'change';
console.log(oldObj.a, newObj.a); //=> 1 "change"

newObj.c.f.g = 'change';
console.log(oldObj.c.f.g, newObj.c.f.g); //=> change change

深度克隆

1
2
3
4
5
6
7
const oldObj = { //=> 原对象
a: 1,
b: ['d','e'],
c: { f: {g: 2} }
};

const newObj = JSON.parse(JSON.stringify(oldObj)); //=> 克隆的新对象

通过上面JSON的parse和stringify看似可以达到深度克隆的效果,但其中却是有坑的。

  1. 它无法实现对函数、RegExp等特殊对象的克隆
  2. 会抛弃对象的constructor,所有的构造函数会指向Object
  3. 对象有循环引用,会报错

对于深度克隆,需要判断对象的类型,来针对相应的类型应用相应的克隆方式

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
const isType = (obj, type) => { //=> 判断对象类型
if (typeof obj !== 'object') return false;
const typeString = Object.prototype.toString.call(obj);
let flag;
switch (type) {
case 'Array':
flag = typeString === '[object Array]';
break;
case 'Date':
flag = typeString === '[object Date]';
break;
case 'RegExp':
flag = typeString === '[object RegExp]';
break;
default:
flag = false;
}
return flag;
};

const clone = parent => { //=> 克隆
// 维护两个储存循环引用的数组
const parents = [];
const children = [];

const _clone = parent => {
if (parent === null) return null;
if (typeof parent !== 'object') return parent;

let child, proto;

if (isType(parent, 'Array')) {
// 对数组做特殊处理
child = [];
} else if (isType(parent, 'RegExp')) {
// 对正则对象做特殊处理
child = new RegExp(parent.source, getRegExp(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (isType(parent, 'Date')) {
// 对Date对象做特殊处理
child = new Date(parent.getTime());
} else {
// 处理对象原型
proto = Object.getPrototypeOf(parent);
// 利用Object.create切断原型链
child = Object.create(proto);
}

// 处理循环引用
const index = parents.indexOf(parent);

if (index != -1) {
// 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
return children[index];
}
parents.push(parent);
children.push(child);

for (let i in parent) {
// 递归
child[i] = _clone(parent[i]);
}

return child;
};
return _clone(parent);
};

请针对 Disqus 开启代理