上一篇,我們從Symbol和是否可枚舉以及屬性描述符的角度分析了ES6下怎么淺拷貝一個對象葛家,發(fā)表在掘金和segmentfault上户辞,從評論看,部分人覺著看不懂癞谒,今天底燎,我們用更簡單的方式來聊聊深拷貝的問題
寫在前面
深拷貝的話題好像從來沒有停止過討論,JavaScript并沒有一個可以實現(xiàn)深拷貝的方法弹砚,我們常見的實現(xiàn)方式是遞歸和JSON.parse(JSON.stringify())
(聽說底層還是用了遞歸)双仍,然而一般庫函數(shù)也只能處理常見的需求(不常見的需求真的存在嗎?真的需要用深拷貝嗎桌吃?真的不承認是你代碼的問題嗎殊校?)。今天读存,我就仔細为流、認真呕屎,細致(也不是很細致),負責(也不敢太保證)的態(tài)度來研究一下怎么實現(xiàn)一個深拷貝吧敬察,雖然一度放棄秀睛,事實也的確是放棄了,但不把這么多天的付出寫出來怎么對得起那個在這個寒冷的冬天忍住瑟瑟發(fā)抖的在鍵盤上敲擊的我...
常見深拷貝
JSON系列化
JSON.parse(JSON.stringify())
的確是一種很簡單易用的方式呢莲祸,可惜的是蹂安,JSON是一個很有原則的男人,他可不會對你言聽計從锐帜。在遇到不安全的JSON值會自動將其忽略田盈,在數(shù)組中則會返回null(以保證單元位置不變)。
不安全的 JSON 值: undefined 缴阎、 function 允瞧、 symbol (ES6+)和包含循環(huán)引用(對象之間相互引用,形成一個無限循環(huán))的 對象 都不符合 JSON 結(jié)構(gòu)標準蛮拔,支持 JSON 的語言無法處理它們
遞歸
上一篇講淺拷貝的時候述暂,我們在開始引入了一個淺拷貝的例子,現(xiàn)在我們把它改成一件簡單的深拷貝建炫。
function deepCopy (obj) {
if (typeof obj !== 'object') {
return
}
var newObj = obj instanceof Array ? [] : {}
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]
}
}
return newObj
}
好像也還不錯畦韭,簡單易懂還能用,一般的場景的確是一種不錯的方法呢肛跌,但是艺配,今天我們來看看不一般的場景。
我們先來挑挑毛惭苌鳌:
-
function
類型沒有處理(大概转唉,或許,應(yīng)該是真的沒必要吧西饵,下面我也并不打算討論這貨酝掩,有興趣的去看看call
、apply
眷柔、bind
) - 循環(huán)引用
- 類型判斷用
typeof
和instanceof
靠譜嗎期虾?(特別注意typeof null
的坑) - 數(shù)組?[]:{},這么簡單驯嘱?不存在的
循環(huán)引用
上面多處說到了循環(huán)引用的問題镶苞,我們先來看看什么是循環(huán)引用:
var a = {}
a.b = a
是的,就是這么一個反人類的存在鞠评,但是卻是我們不能忽略的一個大問題茂蚓。我們是應(yīng)該返回空呢、undefined
呢,還是它的引用聋涨,還是什么呢晾浴?好像沒有標準答案呢,嗯牍白,那就Follow Your Heart吧!
類型判斷
思考一下:
typeof null // "object"
null instanceof Object // false
進行類型判斷是無可避免的脊凰,然而我們似乎并沒有什么完美的方式得到我們需要的類型,我們先來看看幾種常用的方式:
-
typeof
: 返回一個表達式的數(shù)據(jù)類型的字符串茂腥,返回結(jié)果為js基本的數(shù)據(jù)類型狸涌,包括number
,boolean
,string
,object
,undefined
,function
,symbol
-
instanceof
: 判斷一個對象是否為某一數(shù)據(jù)類型,或一個變量是否為一個對象的實例;返回boolean類型最岗。內(nèi)建類型只有通過構(gòu)造器才能用instanceof -
constructor
: 是每一個實例對象都擁有的屬性帕胆,而這個屬性也相當于是一個指針,它指向于創(chuàng)建當前對象的對象 -
Object.prototype.toString.call(obj).slice(8,-1)
: 返回的是類名
typeof
的問題就很明顯了:
typeof null // "object"
typeof function () {} // "function"
typeof [] // "object"
instanceof
考慮一下多全局對象(多個frame
或多個window
之間的交互)般渡,在瀏覽器中懒豹,我們的腳本可能需要在多個窗口之間進行交互。多個窗口意味著多個全局環(huán)境诊杆,不同的全局環(huán)境擁有不同的全局對象歼捐,從而擁有不同的內(nèi)置類型構(gòu)造函數(shù)何陆。這可能會引發(fā)一些問題晨汹。比如,表達式 [] instanceof window.frames[0].Array
會返回false
贷盲,因為 Array.prototype !== window.frames[0].Array.prototype
constructor
屬性得到的僅僅是構(gòu)造函數(shù)淘这,而且是可以被手動更改的,constructor.name
只是返回的構(gòu)造函數(shù)的名字巩剖,它并不返回類名铝穷。
Object.prototype.toString.call
算是比較公認靠譜的方法了吧,然而佳魔,它同樣有可能被人為仿造曙聂,鴨子類型嘛,但它還是比較安全的方式鞠鲜。
鴨子類型: "如果它走起路來像鴨子宁脊,叫起來也是鴨子,那么它就是鴨子"贤姆。動態(tài)類型的語言傾向于你讓它做什么它就是什么
類型分析
討論鋪墊的內(nèi)容應(yīng)該夠細了吧榆苞,接下來我們看看js的復(fù)雜數(shù)據(jù)類型到底有多復(fù)雜。
我們常見的有:
基本包裝類型(Boolean霞捡、String坐漏、Number)、function、Array赊琳、Date
你常見街夭,但你不一定想的起的:
RegExp,Arguments躏筏,Error莱坎、NodeList
你不一定常見,你也不一定知道的:
Blob寸士、File檐什、FileList、ImageData
ES6:
Map弱卡、Set乃正、WeakMap、WeakSet婶博、ArrayBuffer對象瓮具、TypedArray視圖和DataView視圖、Float32Array凡人、Float64Array名党、Int8Array...
或許列舉的少了不少,但是已經(jīng)夠讓人擔憂深克隆的復(fù)雜程度了挠轴,一一實現(xiàn)他們不是一件簡單的事情传睹,甚至是一件完全沒有必要的事情(當然可以讓你了解更多),推薦幾個很優(yōu)秀的方案供參考:
- lodash克隆岸晦,lodash花了大量的代碼來實現(xiàn) ES6 引入的大量新的標準對象欧啤。更厲害的是,lodash 針對存在環(huán)的對象的處理也是非常出色的
- jQuery克隆無法正確深復(fù)制 JSON 對象以外的對象
- 結(jié)構(gòu)化克隆算法
寫在最后
克隆的部分就寫的差不多了启上,本來想寫點Map邢隧、Set的內(nèi)容的,無賴冈在,并沒有找到合適的地方倒慧,MDN、阮一峰的ECMAScript 6 入門都介紹的挺好的包券。
好吧纫谅,就這樣吧,前端界的小學(xué)生兴使,不足之處系宜,還請指正