[javascript] vanilla js 實作 JQuery.extend 方法

若有使用過 JQuery,或許會對 extend 這個方法有些熟悉及基本了解。


先來看看 extend 的用法

jQuery.extend(obja, objb) 方法輸入為兩個參數,此方法會判斷 obja 與 objb 屬性,將 objb 物件所擁有的屬性覆寫/新增至 obja,並回傳 obja。

來看看程式碼比較快理解~

var a = {

    'name': 'John'
};

var b = {

    'name': 'Marry',

    'phone': 0912345678

};

var c = jQuery.extend(a, b);

console.log(a);       // { 'name': 'Marry', 'phone': 0912345678 }

console.log(b);       // { 'name': 'Marry, 'phone': 0912345678 }

console.log(a === b); // false

console.log(a === c); // true

console.log(b === c); //false

從上面的例子可以發現幾個值得注意的地方

  • a 與 b 都存在的屬性,會被 b 覆寫
  • a 與 c 是同一個物件

 

接下來,我們試著用 vanilla js 實作 extend 函式

function extend(a, b) {
    for ( var prop in b ) {
        a[prop] = b[prop];
    }
    return a;
}

var a = {

    'name': 'John'
};

var b = {

    'name': 'Marry',

    'phone': 0912345678

};

var c = extend(a, b);

console.log(a);       // { 'name': 'Marry', 'phone': 0912345678 }

console.log(b);       // { 'name': 'Marry, 'phone': 0912345678 }

console.log(a === b); // false

console.log(a === c); // true

console.log(b === c); //false

上面這段程式碼簡單實作了jQuery 基礎的 extend,但實際的 jquery.extend 提供更多的選項及參數彈性

舉例來說:

function extend(a, b) {
    for ( var prop in b ) {
        a[prop] = b[prop];
    }
    return a;
}
var a = { 
    'second': { 
        'nest1': 1 
    } 
}; 
var b = { 
    'first':1, 
    'second': { 
        'nest2': 2 
    } 
};

var c = extend(a, b);
console.log(c); // {'first': 1, 'second': {'nest2':2}} 

上面這段程式碼, a 與 b 物件皆有 second ,而且 second 底下的屬性是不相同的,但卻直接被覆寫。

也就是沒有 deep extend ( jquery.extend 中有一個 deep copy 的選項)

若是 deep copy 我們理論上會得到以下結果

{'first': 1, 'second': {'nest1': 1, 'nest2':2}}

這部分使用 vanilla js 實作的程式碼就冗長了不少,完整程式碼附在下方

我想就不詳細解釋,大概提以下幾點

  1. 最重要的是利用遞迴的概念往下挖。(有學過 Tree 相關的資料結構應該懂XD)

  2. 第一個參數決定是否 deep,預設 false

  3. js function 內建的 arguments 去抓目前參數(因為第一個參數未必是 Object,可能是 deep)

function extend() {
    var extended = arguments[0];
    var deep = false;
    var i = 0;
    var length = arguments.length;

    if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) {
        deep = arguments[0];
        extended = arguments[1];
        i++;
    }
    var merge = function (obj) {
        for ( var prop in obj ) {
            if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) {
                if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) {
                    extended[prop] = extend( true, extended[prop], obj[prop] );
                } else {
                    extended[prop] = obj[prop];
                }
            }
        }
    };
    for ( ; i < length; i++ ) {
        var obj = arguments[i];
        merge(obj);
    }
    return extended;
}

參考資料:

https://gomakethings.com/vanilla-javascript-version-of-jquery-extend/

Leave a Reply