Javascript拡張オブジェクトextend実装

4311 ワード

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で使うつもりだったが、依存項目がたくさんあることに気づき、いちいち探すのがおっくうだったので、いっそ自分で最初から書いた.
まず,for inを用いてソースオブジェクトを遍歴し,ターゲットオブジェクトの対応する属性を上書きする最も簡単なextend関数を実現すればよいと考えられる.
var extend = function(destination,source) {
    for(var property in source) {
        destination[property] = source[property]
    }
    return destination
}

非常に簡潔で分かりやすく、この実装方式はほとんどの場合のニーズを満たしていますが、この統合が浅いコピーであるという問題があります.マージされた属性にオブジェクトaが含まれている場合、マージ後、destinationはオブジェクトaの参照を持ち、sourceはオブジェクトaの参照もあり、オブジェクトaの属性を変更すると、destinationsourceは同じオブジェクトを参照し、これが浅いコピーになります.深いコピー、すなわち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が比較的完全に実装され、参照に供される.バグがあればコメントを歓迎します.
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