本篇適合前端新人,下面開始......
對於前端新手來說(比如博主),每當對js的對象做操作時,都是一種痛苦,原因就是在於對象的賦值是引用的傳遞,並非值的傳遞,雖然看上去後者賦值給了前者,他們就各奔東西了,但是他們卻緊緊相連,為了解決這一問題,我們需要從根源上來切斷對象賦值時就對象與新對象之間的藕斷絲連......
我們尋找方法時候,找到了拷貝這個方法,就是把要賦值的對象的屬性一個一個加到新對象中去,所以我們得到了clone方法:
function clone (obj) { var news = {} for (var key in obj) { news[key] = obj[key] } return news }
但是新問題又來了,我的新對象並不是空的,我的新對象還有其他屬性,以上這個方法並不適用了,怎麼辦......
對於繼承這個名詞,大家並不陌生,該有的留下,該繼承的加進來,該覆蓋的覆蓋掉。於是我們稍微修改了一下上面的代碼,得到了extend方法:
function extend(target, obj) { for (var key in obj) { target[key] = obj[key] } return target }
我們把目標對象穿進去,把繼承對象中的屬性依依加到目標對象。最後,我們返回目標對象,雖然不比這麼做目標對象已經改變,不過我們還是這樣做吧。
問題總是不斷的發現,現在我們又有問題了,我們在繼承過程中,如果某一個屬性值是一個對象,那麼我們的繼承函數仍然有引用傳遞,這樣一來仍然聯系不斷!怎麼辦呢?這時候大牛們變引發了深拷貝這個名詞,顧名思義,如果對象中還有對象,那麼一層一層的拷貝下去吧,不信你能有10086層對象嵌套。
深拷貝的核心是遞歸繼承,碰到屬性值為對象,就觸發遞歸繼承。在這裡,你可能想到了jQuery等功能庫,的確,他們都有extend方法來實現深拷貝,但我覺得不完美,因為我們需要依靠自己的力量,不能僅僅依賴別人,因此我們又需要繼續探索.....
在我們探索如果深拷貝時,JSON對象缺在偷偷地笑,我問他在笑什麼,他跟我說了這樣的話:
var obj1 = { name: 'xu', age: 21,
native: {
weight: 70,
height: 170
} ... //許多屬性 } var obj2 = clone(obj1) // 淺拷貝,未斷開聯系 var obj2 = JSON.parse(JSON.stringify(obj1)) // 深拷貝,成功斷開
他告訴我說:你的clone是不行滴,我給你兩個方法,分分鐘讓對象斷子絕孫。於是,我就學會了這樣進行深拷貝。但是需求還是遠遠不夠,在現實項目中,我們需要的是在繼承中深拷貝,於是我們繼續探索,終於......
function deepExtend (target, obj) { var clone for (var key in obj) { clone = obj[key] if (typeof clone === 'object') { // 只考慮數組和對象兩種情況 target[key] = JSON.parse(JSON.stringify(clone)) } else { target[key] = clone } } return target }
好吧,我們寫出了上面這個簡單的繼承,不過感覺很瑕疵,萬一我們需要給目標對象繼承多個對象怎麼辦?我們如何控制是否深拷貝?低版本浏覽器ie678中JSON無效怎麼辦?好吧,不要問了,容我再想想。
為了解決一系列的問題,最終我給出了一下繼承方法,此繼承方法類似於jQuery.extend,實際也差不多,不過相比簡單易懂(個人覺得),因為加了大量漢語注釋,適合新手們。
function extend () { // arguments種類 // [deep] 可選,標注是否為深度繼承 // target 第一個對象,則為目標對像 // options 之後的對象,都視為繼承對象 var args = arguments, target = args[0], // 假設第一個參數為目標對象 len = args.length, // 獲取參數總長度 i = 1, // 假設繼承對象從下標為1開始 deep = false, // 初始化為淺拷貝 tar, source, option, key // 如果第一個參數是布爾值,那麼第二個參數做為目標對象 if (typeof target === 'boolean') { deep = target target = args[i++] } // 遍歷繼承對象,並將每一個都繼承到目標對象中 for (; i < len; i++) { option = args[i] for (key in option) { tar = target[key] source = option[key] // 如果為深拷貝並且此時的屬性值為對象,則進行遞歸拷貝 if (deep && typeof source === 'object') { if (!tar) { // 如果目標對象沒有此屬性,那麼創建它 tar = Object.prototype.call(source) === '[object Array]'? []: {} } // 將遞歸拷貝的結果賦值給目標對象 target[key] = assign(deep, tar, source) } else{ // 如果為淺拷貝,直接賦值 target[key] = source } } } return target }
很遺憾本菜只能有這些本事了, 帖子內容不少,大多是廢話,新人可以看一下。