首頁 >web前端 >js教程 >javascript深拷貝(deepClone)詳解

javascript深拷貝(deepClone)詳解

高洛峰
高洛峰原創
2017-01-03 15:59:291162瀏覽

javascript深拷貝是初學者甚至有經驗的開發著,都會經常遇到問題,並不能很好的理解javascript的深拷貝。

深拷貝(deepClone)?

與深拷貝相對的就是淺拷貝,很多初學者在接觸這個感念的時候,是很懵逼的。

為啥要用深拷貝?

在很多情況下,我們都需要給變量賦值,給內存地址一個值,但是在賦值引用值類型的時候,只是共享一個內存區域,導致賦值的時候,還跟之前的值保持一直性。

看一個具體的例子

// 给test赋值了一个对象
var test = {
  a: 'a',
  b: 'b'
};
 
// 将test赋值给test2
// 此时test和test2是共享了同一块内存对象,这也就是浅拷贝
var test2 = test;
 
test2.a = 'a2';
 
test.a === 'a2'// 为true

圖解:


這下就很好理解為什麼引用值類型資料會互相影響問題。

實作

實作一個深拷貝函數,就不得不說javascript的數值類型。

判斷javascript類型

javascript中有以下基本型別

型 描述
undefined undefined型別只有一個值undefined,它是變數未賦值時的值
null 的物件參考
Boolean Boolean有兩種取值true和false
String 它表示文字訊息
Number 它表示數字資訊
Object 它是一系列屬性的無序集合,包含函數Function和陣列Array
使用typeof 是無法判斷function和array的,這裡使用Object.prototype.toString方法。
[預設情況下,每個物件都會從Object上繼承到toString()方法,如果這個方法沒有被這個物件本身或更接近的上層原型上的同名方法覆寫(遮蔽),則呼叫該物件的toString( )方法時會回傳"[object type]",這裡的字串type表示了一個物件類型][1]

function type(obj) {
  var toString = Object.prototype.toString;
  var map = {
    '[object Boolean]' : 'boolean', 
    '[object Number]'  : 'number', 
    '[object String]'  : 'string', 
    '[object Function]' : 'function', 
    '[object Array]'  : 'array', 
    '[object Date]'   : 'date', 
    '[object RegExp]'  : 'regExp', 
    '[object Undefined]': 'undefined',
    '[object Null]'   : 'null', 
    '[object Object]'  : 'object'
  };
  return map[toString.call(obj)];
}

實作deepClone

對於非引用值型別的數值,直接賦值,而對於引用值型別(object)還需要再次遍歷,遞歸賦值。

function deepClone(data) {
  var t = type(data), o, i, ni;
   
  if(t === 'array') {
    o = [];
  }else if( t === 'object') {
    o = {};
  }else {
    return data;
  }
   
  if(t === 'array') {
    for (i = 0, ni = data.length; i < ni; i++) {
      o.push(deepClone(data[i]));
    }
    return o;
  }else if( t === &#39;object&#39;) {
    for( i in data) {
      o[i] = deepClone(data[i]);
    }
    return o;
  }
}

這裡有個點大家要注意下,對於function類型,博主這裡是直接賦值的,還是共享一個內存值。這是因為函數更多的是完成某些功能,有個輸入值和返回值,而且對於上層業務而言更多的是完成業務功能,並不需要真正將函數深拷貝。

但是function類型要怎麼拷貝呢?

其實部落客只想到了用new來操作一下,但function就會執行一遍,不敢想像會有什麼執行結果! o(╯□╰)o!其它暫時還沒有什麼好的想法,歡迎大家指導哦!

到這裡差不多也就實現完了深拷貝,又有人覺的怎麼沒有實現淺拷貝呢?

淺拷貝?

對於淺拷貝而言,可以理解為只操作一個共同的記憶體區域!這裡會存在危險! (.﹏。*)

如果直接操作這個共享的數據,不做控制的話,會經常出現數據異常,被其它部分更改。所以應該不要直接操作資料來源,為資料來源封裝一些方法,來對資料來進行CURD操作。

到這裡估計就差不多了,但是作為一個前端,不僅考慮javascript本身,還得考慮到dom、瀏覽器等。

Element類型

來看下面程式碼,結果會回傳啥呢?

Object.prototype.toString.call(document.getElementsByTagName('div')[0])

答案是[object HTMLDivElement]

有時候保存了dom元素, 一不小心進行深拷貝,上面的深拷貝,上面的深複製有時候拷貝函數就缺少了對Element元素的判斷。而判斷Element元素要使用instanceof來判斷。因為對於不同的標籤,tostring會傳回對應不同的標籤的建構子。

function type(obj) {
  var toString = Object.prototype.toString;
  var map = {
    &#39;[object Boolean]&#39; : &#39;boolean&#39;, 
    &#39;[object Number]&#39;  : &#39;number&#39;, 
    &#39;[object String]&#39;  : &#39;string&#39;, 
    &#39;[object Function]&#39; : &#39;function&#39;, 
    &#39;[object Array]&#39;  : &#39;array&#39;, 
    &#39;[object Date]&#39;   : &#39;date&#39;, 
    &#39;[object RegExp]&#39;  : &#39;regExp&#39;, 
    &#39;[object Undefined]&#39;: &#39;undefined&#39;,
    &#39;[object Null]&#39;   : &#39;null&#39;, 
    &#39;[object Object]&#39;  : &#39;object&#39;
  };
  if(obj instanceof Element) {
    return &#39;element&#39;;
  }
  return map[toString.call(obj)];
}

先通過JSON.stringify一下,然後再JSON.parse一下,就能實現深拷貝。但是資料型別只支援基本數值類型。

var obj = {
  a: &#39;a&#39;, 
  b: function(){console.log(&#39;b&#39;)}
}
 
//在JSON.stringify的时候就会把function给过滤了。
 
JSON.stringify(obj)// "{"a":"a"}"

   

小結

這裡大概總結了一下深拷貝,以及怎麼實現一個深拷貝。在不同的場景下,要根據業務場景,判斷是否需要使用深拷貝。

更多javascript深拷貝(deepClone)詳解相關文章請關注PHP中文網!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn