大家都知道,js的對象是引用類型,如果直接var obj2 = obj,obj2和obj是共享同一個對象實體的,這往往不是我們想要的結果。
官方並沒有給出通用的對象克隆方法:
我們給出以下幾種寫法:
1.通用對象克隆:
function clone(obj, hash = new WeakMap()) { // 解決循環引用 if (hash.has(obj)) { return hash.get(obj) } let temp = null; if (obj instanceof Array) { // 特殊處理數組對象類型 temp = [] hash.set(obj, temp) obj.forEach(item => { temp.push(clone(item, hash)); }) } else if (obj instanceof RegExp) { // 特殊處理正則對象類型 const { source, global, ignoreCase, multiline } = obj; let flags = ''; if (global) flags += 'g'; if (ignoreCase) flags += 'i'; if (multiline) flags += 'm'; temp = new RegExp(source, flags); } else if (obj instanceof Date) { // 特殊處理時間對象類型 temp = new Date(obj.getTime()); } else if (typeof obj === 'object') { // 處理普通對象類型 // 以obj的原型為原型,構造一個新對象 temp = Object.create(obj.__proto__); hash.set(obj, temp) } else { temp = obj; } // 任何對象類型,都遍歷遞歸自身的屬性 if (typeof obj === 'object') { for (let [key, val] of Object.entries(obj)) { temp[key] = clone(val, hash); } } return temp; }
遇到數組對象類型、正則對象類型、時間對象類型、普通對象類型都需要有自己的特殊,最后他們都需要遍歷遞歸他們自身的屬性
原始類型(包括Symbol)純復制就可以了
函數做的淺拷貝(因為如果拷貝函數,只能用eval這個危險的工具了,所以這里函數僅作淺克隆,lodash的cloneDeep對函數也是淺克隆處理)
推薦閱讀:https://juejin.im/post/5b235b726fb9a00e8a3e4e88
ps:這里還有一些坑,無法克隆 Error 對象,無法克隆原型,無法克隆不可枚舉的屬性.... 不過這個深克隆已經可以覆蓋百分之99的場景了!!
2.JSON對象序列化方法
深拷貝,但是有一大堆坑(推薦閱讀:https://juejin.im/post/5abb55ee6fb9a028e33b7e0a):
基礎的5個大坑:
1. 函數不能拷貝
2. Symbol不能拷貝
4. undefined不能拷貝
5. 正則拷貝后變成普通對象
6. 循環引用的對象會報錯
7. 數組的屬性丟失
8. 數組里面的undefined會變成null
9. 會拋棄對象的constructor
。。。。估計還有很多我沒想到的
let a = { age: undefined, sex: Symbol('male'), jobs: function() {}, name: 'yck' }
let b = JSON.parse(JSON.stringify(a)) console.log(b) // {name: "yck"}
正常使用:
var obj = {a:1,b:2} var newObj = JSON.parse(JSON.stringify(obj));
3.dom元素的復制——cloneNode
let div = document.getElementById('box');
let box2 = div.cloneNode(true);
4.es6新方法——Object.assign
淺拷貝
var obj = {a:1,b:2} var newObj = Object.assign({}, obj);
5. MessageChannel的方法
異步的深克隆,但是無法克隆function 、Symbol
let obj = {a: 1,b: 2}
let {port1, port2} = new MessageChannel();
port2.onmessage = ev => console.log(ev.data)
port2.postMessage(obj)
總結:日常使用還是推薦用lodash的cloneDeep
