Javascript擴展對象extend實現

WanJieanZ1 7年前發布 | 12K 次閱讀 JavaScript開發 JavaScript

jQuery的 $.extend 方法是我們在開發中經常用到的方法,用于合并若干個對象,且支持深度拷貝。

最常見的一個使用場景是參數的合并,比如我們要做一個顯示對話框的組件,接收一個 option 對象參數,把它和默認參數 defaultOption 合并,得到新的參數。這樣做的好處就是對 option 字段的拓展非常方便,并且使用者可以只傳部分參數,其他均為默認值,代碼可讀性也比較好。

var showDialog = (function() {
     var defaultOption = {
        title:'',
        width:500,
        close:function(){}
    }
    return function(option) {
        $.extend({},defaultOption,option);
    }
})()

showDialog({
    title:'',
    close:function() {
      console.log('dialog closed')
    }
})

這種模式在很多地方都用到,最常見的我們使用 $.ajax 發起ajax請求,對于傳遞的option也是這樣處理的。

在現在的項目中,由于用的是Vue,避免了繁瑣的Dom操作,所以用不到jQuery提供的dom操作。但是我需要 $.extend 方法。在查看了他的源碼之后,本來打算直接copy過來使用,可是發現它有很多依賴項,懶得一一去找,所以索性自己從頭寫一個。

我們可以考慮首先實現一個最簡單的extend函數,即用 for in 遍歷源對象,覆蓋目標對象的對應屬性即可。

var extend = function(destination,source) {
    for(var property in source) {
        destination[property] = source[property]
    }
    return destination
}

非常簡潔易懂,這種實現方式滿足了大部分情況下的需求,但存在一個問題,就是這種合并是淺拷貝。

如果合并的屬性中含有對象 a ,那么在進行合并之后, destination 擁有的是對象a的引用,而 source 對象也有對象 a 的引用,那么如果我們修改對象 a 的屬性, destination 和 source 都將被修改——它們引用的是同一個對象,這就是淺拷貝。我們想實現深拷貝,即在 destination 中的對象 a 是一份復制品,而不是引用,那么我們需要對對象的賦值做額外的判斷和處理。

var isObjFunc = function(name) {
    var toString = Object.prototype.toString
    return function() {
        return toString.call(arguments[0]) === '[object ' + name + ']'
    } 
}
var isObject = isObjFunc('Object'),
var extend = function(destination,source,isDeep) {
    var obj,copy
    for(var property in source) {
        obj = source[property]
        if(isDeep && isObject(obj) { // 判斷是深拷貝且這個屬性是純對象
            var copy = {}
            destination[property] = extend(copy,obj,isDeep) // 遞歸調用,創建一份obj的拷貝,賦值給destination
        } else {
            destination[property] = obj
        }
    }
    return destination
}

上面的代碼就實現了一個簡單深拷貝。但這里還有一個漏洞,如果是數組的話,創建 copy 的時候應該設置為一個新的空數組,這樣 for in 操作擴展才能正常執行。再參考 jQuery.extend 的實現方式,利用 arguments 處理多個對象合并的情況,最終的代碼如下,較為完整的實現了 extend ,供參考。如果有bug歡迎留言指正。

var extend = (function() {
    var isObjFunc = function(name) {
        var toString = Object.prototype.toString
        return function() {
            return toString.call(arguments[0]) === '[object ' + name + ']'
        } 
    }
    var   isObject = isObjFunc('Object'),
        isArray = isObjFunc('Array'),
        isBoolean = isObjFunc('Boolean')
    return function extend() {
        var index = 0,isDeep = false,obj,copy,destination,source,i
        if(isBoolean(arguments[0])) {
            index = 1
            isDeep = arguments[0]
        }
        for(i = arguments.length - 1;i>index;i--) {
            destination = arguments[i - 1]
            source = arguments[i]
            if(isObject(source) || isArray(source)) {
                console.log(source)
                for(var property in source) {
                    obj = source[property]
                    if(isDeep && ( isObject(obj) || isArray(obj) ) ) {
                        copy = isObject(obj) ? {} : []
                        var extended = extend(isDeep,copy,obj)
                        destination[property] = extended 
                    }else {
                        destination[property] = source[property]
                    }
                }
            } else {
                destination = source
            }
        }
        return destination
    }
})()

測試代碼如下

var a = {name:1}
var b = {name:2}
var c = {name:3}
extend(true,a,b,{name:[a,b,c],value:a})
console.log(a)
console.log(a.name[0] === a) // false
console.log(a.value === a) // false

 

來自:http://www.jianshu.com/p/04b1d88dabf2

 

 本文由用戶 WanJieanZ1 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!