JavaScript中的拷貝

原文地址
要說(shuō) JavaScript 的拷貝問(wèn)題,首先要了解的是 JavaScript 的數(shù)據(jù)類(lèi)型。
在 JavaScript 中數(shù)據(jù)類(lèi)型大致可以分成三類(lèi),分別是基本數(shù)據(jù)類(lèi)型、引用數(shù)據(jù)類(lèi)型和特殊數(shù)據(jù)類(lèi)型。
基本數(shù)據(jù)類(lèi)型有:Number、String、Boolean、
引用數(shù)據(jù)類(lèi)型有:Object(其中包含Array, Object, Date, RegExp)
特殊數(shù)據(jù)類(lèi)型:NullUndefined
ES6 引入了一種新的原始數(shù)據(jù)類(lèi)型 Symbol,表示獨(dú)一無(wú)二的值。所以現(xiàn)在JavaScript中一共有7種數(shù)據(jù)類(lèi)型。

其中特殊類(lèi)型Undefined是變量聲明了但是沒(méi)有賦值,Null表示一個(gè)無(wú)的對(duì)象是一個(gè)Object,這兩者在判斷語(yǔ)句中都會(huì)被轉(zhuǎn)成false,其實(shí)都是表示沒(méi)有,但為什么要有兩個(gè),說(shuō)起來(lái)比較復(fù)雜,可以看看阮老師的文章undefined與null的區(qū)別

繼續(xù)往下說(shuō),下面簡(jiǎn)單說(shuō)說(shuō)基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型的區(qū)別。
最大的區(qū)別就是,基本數(shù)據(jù)類(lèi)型的變量標(biāo)識(shí)符和變量值都存在棧內(nèi)存,而引用類(lèi)型在棧內(nèi)存中存儲(chǔ)的是變量標(biāo)識(shí)符和指針,然后在堆內(nèi)存中保存對(duì)象內(nèi)容。如下圖:

基本類(lèi)型引用類(lèi)型存儲(chǔ)示意圖

現(xiàn)在開(kāi)始說(shuō)拷貝的問(wèn)題,基本數(shù)據(jù)類(lèi)型,我們直接

let a = 1;
let a1 = a;

就可以了。而如果是引用類(lèi)型這樣的話

let b = {a: 1};
let b1 = b;

此時(shí)bb1的值就會(huì)互相影響也就是改變了他們中任何一個(gè)值,另外一個(gè)值也會(huì)相應(yīng)的改變。
來(lái)個(gè)圖:

基本類(lèi)型和引用類(lèi)型的普通拷貝

由此可見(jiàn)使用=的方式對(duì)于基本類(lèi)型是有效的而對(duì)于引用類(lèi)型是不可以的。

在jQuery中我們可以直接使用jQuery的extend()方法來(lái)進(jìn)行深拷貝。

使用原生JavaScript我們應(yīng)該這么做呢?

如果是Array,且數(shù)組中的數(shù)據(jù)都是基本數(shù)據(jù)類(lèi)型,我們可以利用數(shù)組中的slice()方法來(lái)返回一個(gè)新的數(shù)組,像這樣:

let arrA = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let arrA1 = arrA.slice();

如果是Object的keyValuePair,且對(duì)應(yīng)的value都是基本數(shù)據(jù)類(lèi)型,則可以用Object.assign()來(lái)操作。

let a = {a: 1};
let a1 = Object.assign({}, a);

上述兩種方法,只能在特定的情況下有用,一旦數(shù)組的值是引用類(lèi)型或者對(duì)象中value的值是一個(gè)引用類(lèi)型,就不起作用了。如果遇到復(fù)雜的數(shù)據(jù)類(lèi)型該怎么進(jìn)行深拷貝呢?以前在不少帖子里看過(guò)說(shuō)使用JSON來(lái)做個(gè)中轉(zhuǎn),類(lèi)似于這樣:

let a = {a: {a : {a: 1}}};
let a1 = JSON.parse(JSON.stringify(a));

這個(gè)方法好像很無(wú)敵,貌似無(wú)論多么復(fù)雜的數(shù)據(jù)類(lèi)型都能深拷貝,但是一旦遇到key對(duì)應(yīng)的value是Function或者其他JSON不支持的類(lèi)型就不行了,可以參照stackoverflow上的這個(gè)回答javascript deep copy using JSON

所以在JavaScript中深拷貝是一件非常費(fèi)事的事情。大部分情況下我們并不需要深拷貝,再仔細(xì)看看自己寫(xiě)的函數(shù)非得深拷貝不可嗎?

恩,如果你非要!

  1. 看看jQuery extend()方法的具體實(shí)現(xiàn),這里有個(gè)參考資料jQuery源碼
  2. 使用lodash的cloneDeep()方法實(shí)現(xiàn)比較復(fù)雜,當(dāng)然也比較好
  3. 再貼一個(gè)stackoverflow上的回答How to Deep clone in javascript
function clone(item) {
    if (!item) { return item; } // null, undefined values check

    var types = [ Number, String, Boolean ], 
        result;

    // normalizing primitives if someone did new String('aaa'), or new Number('444');
    types.forEach(function(type) {
        if (item instanceof type) {
            result = type( item );
        }
    });

    if (typeof result == "undefined") {
        if (Object.prototype.toString.call( item ) === "[object Array]") {
            result = [];
            item.forEach(function(child, index, array) { 
                result[index] = clone( child );
            });
        } else if (typeof item == "object") {
            // testing that this is DOM
            if (item.nodeType && typeof item.cloneNode == "function") {
                var result = item.cloneNode( true );    
            } else if (!item.prototype) { // check that this is a literal
                if (item instanceof Date) {
                    result = new Date(item);
                } else {
                    // it is an object literal
                    result = {};
                    for (var i in item) {
                        result[i] = clone( item[i] );
                    }
                }
            } else {
                // depending what you would like here,
                // just keep the reference, or create new object
                if (false && item.constructor) {
                    // would not advice to do that, reason? Read below
                    result = new item.constructor();
                } else {
                    result = item;
                }
            }
        } else {
            result = item;
        }
    }

    return result;
}
  1. 自己擼一個(gè)...
  2. 待我有空擼一個(gè)(逃
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 原文: https://github.com/ecomfe/spec/blob/master/javascript...
    zock閱讀 3,486評(píng)論 2 36
  • 對(duì)象的創(chuàng)建與銷(xiāo)毀 Item 1: 使用static工廠方法,而不是構(gòu)造函數(shù)創(chuàng)建對(duì)象:僅僅是創(chuàng)建對(duì)象的方法,并非Fa...
    孫小磊閱讀 2,184評(píng)論 0 3
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類(lèi)相關(guān)的語(yǔ)法,內(nèi)部類(lèi)的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 34,696評(píng)論 18 399
  • 去年的微信公眾號(hào)調(diào)研數(shù)據(jù)里顯示,微信日均活躍人數(shù)為5.7億;而在機(jī)構(gòu)對(duì)微信公眾號(hào)進(jìn)行調(diào)研的數(shù)據(jù)顯示,整個(gè)季度微信公...
    Sunsoft閱讀 2,189評(píng)論 3 8
  • 很多朋友會(huì)覺(jué)得自己的女人緣很差,異性朋友很少,下面小編就來(lái)告訴你怎樣博得女生的信任。 其實(shí)女孩是很脆弱的,很容易受...
    幸福空間閱讀 886評(píng)論 0 0

友情鏈接更多精彩內(nèi)容