原文:ES6時代,你真的會克隆對象嗎(二)前端
上一篇,咱們從Symbol和是否可枚舉以及屬性描述符的角度分析了ES6下怎麼淺拷貝一個對象,發表在掘金和segmentfault上(其餘地方也能看到這篇文章,雖然並無人問過個人意見,即使我是贊成的,可是連一個「轉」字,一個原文連接都不給,這就很讓人傷心了),從評論看,部分人覺着看不懂,今天,咱們用更簡單的方式來聊聊ES6下深拷貝的問題jquery
深拷貝的話題好像歷來沒有中止過討論,JavaScript並無一個能夠實現深拷貝的方法,咱們常見的實現方式是遞歸和JSON.parse(JSON.stringify())
(據說底層仍是用了遞歸),然而通常庫函數也只能處理常見的需求(不常見的需求真的存在嗎?真的須要用深拷貝嗎?真的不認可是你代碼的問題嗎?)。今天,我就仔細、認真,細緻(也不是很細緻),負責(也不敢太保證)的態度來研究一下怎麼實現一個深拷貝吧,雖然一度放棄,事實也的確是放棄了,但不把這麼多天的付出寫出來怎麼對得起那個在這個寒冷的冬天忍住瑟瑟發抖的在鍵盤上敲擊的我...git
JSON.parse(JSON.stringify())
的確是一種很簡單易用的方式呢,惋惜的是,JSON是一個頗有原則的男人,他可不會對你言聽計從。在遇到不安全的JSON值會自動將其忽略,在數組中則會返回null(以保證單元位置不變)。es6
不安全的 JSON 值: undefined 、 function 、 symbol (ES6+)和包含循環引用(對象之間相互引用,造成一個無限循環)的 對象 都不符合 JSON 結構標準,支持 JSON 的語言沒法處理它們
上一篇講淺拷貝的時候,咱們在開始引入了一個淺拷貝的例子,如今咱們把它改爲一件簡單的深拷貝。github
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 }
好像也還不錯,簡單易懂還能用,通常的場景的確是一種不錯的方法呢,可是,今天咱們來看看不通常的場景。算法
咱們先來挑挑毛病:segmentfault
function
類型沒有處理(大概,或許,應該是真的不必吧,下面我也並不打算討論這貨,有興趣的去看看call
、apply
、bind
)typeof
和instanceof
靠譜嗎?(特別注意typeof null
的坑)上面多處說到了循環引用的問題,咱們先來看看什麼是循環引用:數組
var a = {} a.b = a
是的,就是這麼一個反人類的存在,可是倒是咱們不能忽略的一個大問題。咱們是應該返回空呢、undefined
呢,仍是它的引用,仍是什麼呢?好像沒有標準答案呢,嗯,那就Follow Your Heart吧!瀏覽器
思考一下:安全
typeof null // "object" null instanceof Object // false
進行類型判斷是無可避免的,然而咱們彷佛並無什麼完美的方式獲得咱們須要的類型,咱們先來看看幾種經常使用的方式:
typeof
: 返回一個表達式的數據類型的字符串,返回結果爲js基本的數據類型,包括number
,boolean
,string
,object
,undefined
,function
,symbol
instanceof
: 判斷一個對象是否爲某一數據類型,或一個變量是否爲一個對象的實例;返回boolean類型。內建類型只有經過構造器才能用instanceofconstructor
: 是每個實例對象都擁有的屬性,而這個屬性也至關因而一個指針,它指向於建立當前對象的對象Object.prototype.toString.call(obj).slice(8,-1)
: 返回的是類名typeof
的問題就很明顯了:
typeof null // "object" typeof function () {} // "function" typeof [] // "object"
instanceof
考慮一下多全局對象(多個frame
或多個window
之間的交互),在瀏覽器中,咱們的腳本可能須要在多個窗口之間進行交互。多個窗口意味着多個全局環境,不一樣的全局環境擁有不一樣的全局對象,從而擁有不一樣的內置類型構造函數。這可能會引起一些問題。好比,表達式 [] instanceof window.frames[0].Array
會返回false
,由於 Array.prototype !== window.frames[0].Array.prototype
constructor
屬性獲得的僅僅是構造函數,並且是能夠被手動更改的,constructor.name
只是返回的構造函數的名字,它並不返回類名。
Object.prototype.toString.call
算是比較公認靠譜的方法了吧,然而,它一樣有可能被人爲仿造,鴨子類型嘛,但它仍是比較安全的方式。
鴨子類型: "若是它走起路來像鴨子,叫起來也是鴨子,那麼它就是鴨子"。動態類型的語言傾向於你讓它作什麼它就是什麼
討論鋪墊的內容應該夠細了吧,接下來咱們看看js的複雜數據類型到底有多複雜。
咱們常見的有:
基本包裝類型(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...
或許列舉的少了很多,可是已經夠讓人擔心深克隆的複雜程度了,一一實現他們不是一件簡單的事情,甚至是一件徹底沒有必要的事情(固然可讓你瞭解更多),推薦幾個很優秀的方案供參考:
克隆的部分就寫的差很少了,原本想寫點Map、Set的內容的,無賴,並無找到合適的地方,MDN、阮一峯的ECMAScript 6 入門都介紹的挺好的。
好吧,就這樣吧,前端界的小學生,不足之處,還請指正