天天看點

JavaScript 複制對象

在JavaScript這門語言中,資料類型分為兩大類:基本資料類型和複雜資料類型。基本資料類型包括Number、Boolean、String、Null、String、Symbol(ES6 新增),而複雜資料類型包括Object,而所有其他引用類型(Array、Date、RegExp、Function、基本包裝類型(Boolean、String、Number)、Math等)都是Object類型的執行個體對象,是以都可以繼承Object原型對象的一些屬性和方法。

而對于基本資料類型來說,複制一個變量值,本質上就是copy了這個變量。一個變量值的修改,不會影響到另外一個變量。看一個簡單的例子。

而對于複雜資料類型來說,同基本資料類型實作的不太相同。對于複雜資料類型的複制,要注意的是,變量名隻是指向這個對象的指針。當我們将儲存對象的一個變量指派給另一個變量時,實際上複制的是這個指針,而兩個變量都指向都一個對象。是以,一個對象的修改,會影響到另外一個對象。

有一副很形象的圖描述了複雜資料類型複制的原理

JavaScript 複制對象

同理,在複制一個數組時,變量名隻是指向這個數組對象的指針;在複制一個函數時,函數名隻是指向這個函數對象的指針

是以,我們應該如何實作對象的深淺複制?

在JavaScript中,複制對象分為兩種方式,淺複制和深複制。

淺複制沒有辦法去真正的去複制一個對象,而隻是儲存了對該對象的引用;而深複制可以實作真正的複制一個對象。

在ES6中,Object對象新增了一個assign方法,可以實作對象的淺複制。這裡談談Object.assign方法的具體用法,因為稍後會分析jQuery的extend方法,實作的原理同Object.assign方法差不多

Object.assign的第一個參數是目标對象,可以跟一或多個源對象作為參數,将源對象的所有可枚舉(`emuerable` === true)複制到目标對象。這種複制屬于淺複制,複制對象時隻是包含對該對象的引用。<code>Object.assign(target, [source1, source2, ...])</code>

如果目标對象與源對象有同名屬性,則後面的屬性會覆寫前面的屬性

如果隻有一個參數,則直接傳回該參數。即<code>Object.assign(obj) === obj</code>

如果第一個參數不是對象,而是基本資料類型(Null、Undefined除外),則會調用對應的基本包裝類型

如果第一個參數是Null和Undefined,則會報錯;如果Null和Undefined不是位于第一個參數,則會略過該參數的複制

要實作對象的淺複制,可以使用Object.assign方法

不過對于深複制來說,Object.assign方法無法實作

從上面代碼中可以看出,source2對象中e屬性的改變,仍然會影響到obj對象

在實際的開發項目中,前後端進行資料傳輸,主要是通過JSON實作的。JSON全稱:JavaScript Object Notation,JavaScript對象表示法。

JSON對象下有兩個方法,一是将JS對象轉換成字元串對象的JSON.stringify方法;一個是将字元串對象轉換成JS對象的JSON.parse方法。

這兩個方法結合使用可以實作對象的深複制。也就是說,當我們需要複制一個obj對象時,可以先調用JSON.stringify(obj),将其轉換為字元串對象,然後再調用JSON.parse方法,将其轉換為JS對象。就可以輕松的實作對象的深複制

當然,使用這種方式實作深複制有一個缺點就是必須給JSON.parse方法傳入的字元串必須是合法的JSON,否則會抛出錯誤

jQuery.extend對象,對使用jQuery超過一定時間的朋友來說并不預設。這個$.extend方法可以用來擴充jQuery的全局對象,而$.fn.extend方法可以用來擴充執行個體對象。fn實際上是prototype對象的别名,是以,擴充執行個體對象的方法實際上就是在jQuery原型對象上添加一些方法。

$.extend方法不僅可以用來寫jQuery插件,同樣的,它可以用來實作對象的深淺複制。(使用$.extend與$.fn.extend實作深淺複制都可以,唯一的差别就是this的指向性不同)

在具體分析源代碼之前,我在源碼中看到的$.extend方法的一些特點

當不接受任何參數時,直接傳回一個空對象

當隻有一個參數時(這個參數可以任何資料類型(Null、Undefined、Boolean、String、Number、Object)),會傳回this對象,這裡會分為兩種情況。如果用$.extend,會傳回jQuery對象;如果用$.fn.extend,會傳回jQuery的原型對象。

當接收兩個參數時,并且第一個參數是Boolean值時,也會傳回一個空對象。如果第一個參數不是Boolean值,那麼會将源對象複制到目标對象

當接收三個參數以上時,可以分為兩種情況。如果第一個參數是Boolean值表示深淺複制,那麼目标對象會移動到第二個參數,源對象會移動到第三個參數。(目标對象、源對象和Object.assign方法中的相同)。如果第一個參數不是Boolean值,那麼用法與Object.assign方法正常的複制相同。

在循環源對象的過程中,任何資料類型為Null、Undefined或者源對象是一個空對象時,在複制的過程中都會被忽略。

如果源對象和目标對象具有同名的屬性,則源對象的屬性會覆寫掉目标對象中的屬性。如果同名屬性是一個對象的話,則會在deep=true等其他條件下向目标對象的該同名對象添加屬性

下面貼出jQuery-2.1.4中jQuery.extend實作方式的源代碼

是以,可以針對分析過後的源碼,給出一些例子

是以,可以根據$.extend方法,寫出一個通用的實作對象深淺複制的函數,copyObject函數唯一的不同就是當i === arguments.length屬性時,copyObject函數直接傳回了target對象

}

本文轉自帥氣的頭頭部落格51CTO部落格,原文連結http://blog.51cto.com/12902932/1925711如需轉載請自行聯系原作者

sshpp

繼續閱讀