var a = { a: 'a' } var b = a b.a = 'b' a.a // 'b'
由於js中的對象(引用數據類型)保存在堆中,而多個變量引用同一塊堆中的內容時,其中一個修改,則會影響全部引用改內容的變量;有時候咱們不但願這樣es6
淺拷貝,對目標對象進行遍歷,而後將其屬性逐一拷貝至新建對象中數組
function shallowCopy(source) { const obj = {} for (let i in source) { obj[i] = source[i] } return obj }
測試一下函數
var obj = { a: 1, b: [1, 2, 3], c: {c1: 4} } var obj1 = shallowCopy(obj) obj1.a = 11 obj1.b[0] = 11 obj1.c.c1 = 44 obj.a // 1 obj.b[0] // 11 obj.c.c1 // 44
測試結果
淺拷貝適用於對象中全部屬性都爲基礎數據類型時,若是源對象包含有引用類型的屬性,則淺拷貝沒法切斷與源對象的關係
es6提供了Object.assign,功能與之相似
還有展開運算符: var obj1 = {...obj}性能
針對方法一的問題,提出新的方法:
利用JSON.stringify + JSON.parse 進行對象的序列化/反序列化進行對象的拷貝測試
var obj1 = JSON.parse(JSON.stringify(obj)) obj1.a = 11 obj1.b[0] = 11 obj1.c.c1 = 44 obj.a // 1 obj.b[0] // 1 obj.c.c1 // 4
方法二彷佛完美得解決了方法一遺留的問題,再進行測試優化
function Person(age) { this.age = age } var p = new Person(3) var obj = { a: new Date(), b: /abc/igmuy, c() {}, d: new Array(2), e: p, } var obj1 = JSON.parse(JSON.stringify(obj)) obj1.a // '2019-12-18T08:40:21.650Z' 實際上和調用Date對象的toISOString方法一致,返回的爲時間字符串 obj1.b // {} RegExp對象不會被正確拷貝 obj1.c // undefined 函數不會被正確拷貝 obj1.d // [null, null] 數組空位不會被正確拷貝 obj1.e // obj1.e.constructor指向Object,即原型丟失
還有一種狀況,包含循環引用過的對象使用JSON序列化會拋出異常this
var obj = {} obj.a = obj var obj1 = JSON.parse(JSON.stringify(obj)) // 結果會拋出異常
使用JSON序列化/反序列化實現拷貝的侷限性prototype
以上幾點雖然是該方法的侷限,但一般該方法能知足大多數場景,也不失爲一個簡便的方法code
針對方法一沒法對引用類型屬性進行拷貝和方法二存在的侷限,能夠針對不一樣狀況,進行鍼對性處理regexp
// 類型判斷方法 const isType = (source, type) => Object.prototype.toString.call(source).slice(8, -1).toLowerCase() === type.toLowerCase() const deepClone = source => { const sources = [] const children = [] const _deep = source => { if (typeof source !== 'object' || isType(source, 'null') || isType(source, 'undefined') ) {return source} let child; if (isType(source, 'date')) { // 處理date類型 child = new Date(source.getTime()) } else if (isType(source, 'regexp')) { // 處理正則 child = new RegExp(source.source, source.flags) child.lastIndex = source.lastIndex } else if (isType(source, 'array')) { // 處理數組 child = [] } else { const proto = Object.getPrototypeOf(source) child = Object.create(proto) } // 處理循環引用 const index = sources.indexOf(source) if (index >= 0) { return children[index] } sources.push(source) children.push(child) for (let i in source) { child[i] = _deep(source[i]) } return child } return _deep(source) }
該方法解決了前兩個方法中存在的一些問題
雖然方法三解決了一些問題,但同時引入了一些問題: 因爲使用了遍歷遞歸的方法,在性能方面會有一些問題,有沒有辦法優化呢?
。。。