浅拷贝和深拷贝的实现方式

浅拷贝

循环遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const shallowCopy = (obj) => {
let result = {};

for(let property in obj) {
obj.hasOwnProperty(property) && (result[property] = obj[property]);
}

return result;
}

const obj = { a: 1, b: [2, 3], c: new Date(), d: new RegExp('^/d{1, 6}') };

console.log(obj);
setTimeout(() => console.log(shallowCopy(obj)), 1000);

Object.assign()

1
2
3
4
const obj = { a: {}, b: [2, 3], c: new Date(), d: new RegExp('^/d{1, 6}') };

console.log(obj);
setTimeout(() => console.log(Object.assign({}, obj)), 1000);

深拷贝

JSON.parse()

针对纯 JSON 数据对象的深拷贝,可以使用 JSON 全局对象的 parsestringify 方法来实现

不过这种方法有一定的局限性,它能正确处理的对象只有 Number, String, Boolean, Array, 及扁平对象,即那些能够被 json 直接表示的数据结构

1
2
3
4
5
6
const deepCopy = obj => JSON.parse(JSON.stringify(obj));

const obj = { a: 1, date: new Date()};

console.log(obj);
setTimeout(() => console.log(deepCopy(obj)), 1000);

mark

Structured Clone

Structured Clone 是一种已存在的运算法则,它可以将对象从一种场景传递至另一场景,如通过 postMessage 方法可以发送信息至其他 windowwebWorker;它不仅可以处理循环对象,还支持一系列的数据类型

我们可以通过这种方式来间接地实现深拷贝,下面介绍具体的实现方法:

MessageChannel

Channel Messaging 可以创建消息通道,并通过消息通道的端口号来实现数据传输;发送和接受这个过程,就实现了对象的深拷贝

局限性:这种方式是异步的,有时候我们需要同步拷贝

1
2
3
4
5
6
7
8
9
10
11
12
const structuralClone = (obj) => {
return new Promise(resolve => {
const { port1, port2 } = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);
});
}

let obj = { a: { status: false } },
_obj = null;

structuralClone(obj).then(obj => _obj = obj);

mark

History API

众所周知,history.pushState() 方法需要传递一个 state 状态对象;我们可以通过赋值,然后再取值的方式来实现深拷贝

当然,为了不产生新的历史记录,可以使用 history.replaceState() 方法

局限性:IOS9.3 中 Safari 限制 30s 内至多只能调用 100 次 replaceState() 方法

1
2
3
4
5
6
7
8
9
10
11
12
const structuralClone = (obj) => {
const oldState = history.state;
history.replaceState(obj, document.title);

const copy = history.state;
history.replaceState(oldState, document.title);

return copy;
}

const obj = { a: { status: false } },
_obj = structuralClone(obj);

mark

Notification API

在发送桌面通知时,需要传递 data 数据,我们可以在这个 data 属性上做文章

局限性:这种方式需要通过浏览器授权,效率上会低一些;另外,Safari 不支持 Notification.data

1
2
3
4
5
6
const structuralClone = (obj) => {
return new Notification('', { data: obj, silent: true }).data;
}

const obj = { a: { status: false } },
_obj = structuralClone(obj);

mark

参考