JavaScript 實現深拷貝

來源:互聯網
上載者:User

JavaScript 實現深拷貝


寫該系列的起因是在網上遇到一套比較不錯的應試題目,涉及到了前端的各個角落,以及模組化,HTTP,後端等。題目在此 前端題目

第一題,就來討論一下關於 JS 實現深拷貝的問題。

 
  1. var obj = { a: 1 };
  2. var obj2 = obj;

  3. obj2.a = 3;
  4. obj.a; // 3

這是簡單的對象引用,這僅僅是將對象的引用地址簡單的複製了一份給予變數 obj2,而並不是將真正的對象複製了一份,原對象依舊是只有一個。影響則為,當我修改 obj2 內部的屬性或者添加新屬性時會影響 obj ,因為兩個變數的值為同一個對象的地址引用,即兩者指向的是同一個對象。

這就是所謂深拷貝的意義所在,不是拷貝引用地址,而是實實在在的複製一份新對象。而目前標準中還未提供一個類似的原生方法(主要是由於需求不大,JS 本身已經可以處理的很好了)。所以對於深拷貝,主要遇到的問題就是對於數組,對象以及方法的複製。


對象:一般來說不考慮原型鏈上對象的屬性,均是使用 for in 遍曆並且使用,由於瀏覽器的五花八門,還需要使用 hasOwnPrototype 來進行過濾下。當然也需要考慮某屬性值為 對象,數組,函數等情況。

數組:對於數組來說,也可以如對象一樣 for in 迴圈遍曆,不過通常情況是使用長度進行迴圈,對空數組進行不斷的 push。

函數:對於一般庫來說,一些深拷貝函數基本都不進行處理,實在需要處理的可以調用函數的 toString,再使用 eval 或者 Function 進行處理,得到新的函數。

然後上個執行個體吧,是自己正在寫的庫中的簡單的一個拷貝方法 mix,由於庫的原因擴充了一些功能以及對於方法不做複製處理,使用外部的一個方法 type,是用來判斷區分類型,如 array object function。


  1. /** 對象擴充 mix
  2. *
  3. * @method mix 不擴充原型屬性
  4. * @param {obj} receiver 可選 擴充的目標對象 如果無 則擴充到外圍對象
  5. * @param {obj} obj 必選 要擴充到目標對象的對象資料
  6. * @param {boolean} ride 可選 主要是標識是否覆蓋原有對象屬性 預設為true
  7. * @param {boolean} deep 可選 主要是標識是否需要簡單的深度拷貝 預設為false
  8. *
  9. * @return {Object} 返回目標對象
  10. *
  11. */
  12. varhasOwn = Object.prototype.hasOwnProperty;
  13. function mix(receiver, obj){
  14. var args = [].slice.call(arguments), key, i = 1,deep, ride, value, valueType;

  15. if( typeof args[args.length-2] === "boolean" ){
  16. deep = args.pop();
  17. ride = args.pop();
  18. }else{
  19. ride = (typeof args[args.length-1] === "boolean")?args.pop():true;
  20. deep = false;
  21. if(args.length < 2){
  22. receiver = ( this !== global ) ? this : {};
  23. if( args.length === 0 ){
  24. return receiver;
  25. }
  26. }
  27. }

  28. while( obj = args[ i++ ] ){
  29. for( key in obj ){
  30. if( hasOwn.call(obj, key) ){
  31. if( ride ||!(key in receiver) ){
  32. value = obj[key];
  33. valueType = type(value);
  34. if( deep && ( valueType==="object")){
  35. receiver[key]={};
  36. mix(receiver[key], value, ride, deep);
  37. }else if( deep && ( valueType==="array" )){
  38. receiver[key]=[];
  39. mix(receiver[key], value, ride, deep);
  40. }else{
  41. receiver[key] = obj[key];
  42. }
  43. }
  44. }
  45. }
  46. }
  47. return receiver;
  48. }


對於 type 函數,原始碼如下

 
  1. // 類型判定對象
  2. var class2type = {
  3. "[objectHTMLDocument]" : "document",
  4. "[objectHTMLCollection]" : "nodeList",
  5. "[objectStaticNodeList]" : "nodeList",
  6. "[objectIXMLDOMNodeList]" : "nodeList",
  7. "null" : "null",
  8. "NaN" : "NaN",
  9. "undefined" : "undefined"
  10. };

  11. "Boolean, Number, String, Function, Array, Date, RegExp, Document, Arguments, NodeList"
  12. .replace(/[^, ]+/g, function( type ){
  13. class2type["[object " + type + "]"] = type.toLowerCase();
  14. } );

  15. // 類型判定
  16. function type( obj, isType ){
  17. var key = ((obj == null || obj !== obj ) ? obj + "" : Object.prototype.toString.call( obj )),
  18. result;

  19. if( typeof(result = class2type[ key ]) !== "string" ){
  20. if( obj.nodeType === 9 ){
  21. result = class2type["Document"];
  22. }else if( obj.item && typeof obj.length === "number" ){
  23. result = class2type["NodeList"];
  24. }else{
  25. result = key.slice(8, -1);
  26. }
  27. }


  28. if( isType ){
  29. return result === isType.toLowerCase;
  30. }


  31. return result;
  32. }


這就是簡單的對於 JS 實現深拷貝的例子,有什麼錯誤,歡迎指出。







聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.