從extend函數看JavaScript的深度復制


  Javascript Pattern的Code Reuse Patterns中有一個小節叫做Inheritance by Copying Properties.仔細研究后發現其實這里提到的Copying Properties就是JS中的深度復制。

  先看一下Javascript Pattern中關於深度復制的實現:

function extendDeep(parent, child) {
    var i,
        toStr = Object.prototype.toString,
        astr = "[object Array]";
    child = child || {};
    for (i in parent) {
        if (parent.hasOwnProperty(i)) {
            if (typeof parent[i] === "object") {
                child[i] = (toStr.call(parent[i]) === astr) ? [] : {};
                extendDeep(parent[i], child[i]);
            } else {
                child[i] = parent[i];
            }
        }
    }
    return child;
}

 

看上面的代碼遇到的第一個問題就是關於Object.prototype.toString的應用,在ES5中關於Object.prototype.toString有下面的描述:

When the toString method is called, the following steps are taken:

  1. If the this value is undefined, return "[object Undefined]".
  2. If the this value is null, return "[object Null]".
  3. Let O be the result of calling ToObject passing the this value as the argument.
  4. Let class be the value of the [[Class]] internal property of O.
  5. Return the String value that is the result of concatenating the three Strings "[object "class, and "]"

通過上面的描述,我們不難發現,toString()完成的工作,除去this是undefined和null的情況,將首先調用ToObject方法,這個方法的返回值就是,this對應的Primitive object或者是this本身。接着又是[[Class]]的理解,在Javascript中所有的本地對象都有一個內部屬性[[Class]],這個屬性包含了一個ES定義的關於對象分類的字符值,這個字符值可能的值有以下幾種:

  • "Object"
  • "Array"
  • "Function"
  • "Date"
  • "RegExp"
  • "String"
  • "Number"
  • "Boolean"
  • "Error" for error objects such as instances of ReferenceErrorTypeErrorSyntaxError,Error, etc
  • "Math" for the global Math object
  • "JSON" for the global JSON object defined on the ECMAScript 5th Ed. spec.
  • "Arguments" for the arguments object (also introduced on the ES5 spec.)
  • "null" (introduced just a couple of days ago in the ES5 errata)
  • "undefined"

所以通過上面的描述,不難得出結論:Object.prototype.toString.call(temptValue),返回的是一個字符串,這個字符串的值是

"[object "+this.[[Class]]+"]"

至於為什么不用instanceof 和constructor 檢測array,具體詳見http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/

接着說深度復制,因為在JavaScript中對象是被當做引用來進行傳遞的,所以如果僅僅通過復制的方式將一個對象覆蓋另外一個對象,這樣就會造成當child對象的一個屬性改變時,父對象的對應屬性也會發生改變。

理解了這一思路后,上面關於extendDeep的實現就很好理解了,它的最終的思想就是賦值的都不是對象,所以會有遞歸的調用和判斷,直到不是數組或者是對象。

if (typeof parent[i] === "object") {
                child[i] = (toStr.call(parent[i]) === astr) ? [] : {};
                extendDeep(parent[i], child[i]);
            } else {
                child[i] = parent[i];
            }

下面是jQuery中extend函數的實現

// Recurse if we're merging plain objects or arrays
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                    if ( copyIsArray ) {
                        copyIsArray = false;
                        clone = src && jQuery.isArray(src) ? src : [];

                    } else {
                        clone = src && jQuery.isPlainObject(src) ? src : {};
                    }

                    // Never move original objects, clone them
                    target[ name ] = jQuery.extend( deep, clone, copy );

                // Don't bring in undefined values
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM