jQueryの拡張メソッドに相当するJavaScript


92

バックグラウンド

configオブジェクトを引数として取る関数があります。関数内にもdefaultオブジェクトがあります。これらの各オブジェクトには、基本的に関数内の残りのコードの設定として機能するプロパティが含まれています。configオブジェクト内のすべての設定を指定する必要がないようにするために、jQueryのextendメソッドを使用して、新しいオブジェクトに、オブジェクトで指定されていない場合settingsdefaultオブジェクトのデフォルト値を入力しconfigます。

var config = {key1: value1};
var default = {key1: default1, key2: default2, key 3: default 3};

var settings = $.extend(default, config);

//resulting properties of settings:
settings = {key1: value1, key2: default2, key 3: default 3};

問題

これはうまく機能しますが、jQueryを必要とせずにこの機能を再現したいと思います。平易なol 'javascriptでこれを行うための同様にエレガントな(またはそれに近い)手段はありますか?


編集:重複しない正当化

この質問は、「2つのJavaScriptオブジェクトのプロパティを動的にマージするにはどうすればよいですか?」の質問の複製ではありません。その質問は、2つの個別のオブジェクトからすべてのキーと値を含むオブジェクトを作成したいだけですが、両方のオブジェクトがすべてではなく一部のキーを共有し、どのオブジェクトが優先される場合に、これを行う方法について具体的に説明します(デフォルト)キーが重複している場合の結果のオブジェクト。さらに具体的には、jQueryのメソッドを使用してこれを実現し、jQueryを使用せずにこれを実行する別の方法を見つけたかったのです。両方の質問に対する答えの多くは重複していますが、それは質問自体が同じであることを意味しません。


8
jQueryが行う方法を盗むだけjames.padolsey.com/jquery/#v=1.6.2&fn=jQuery.extend :-P
Rocket Hazmat

良いアイデアの@RocketHazmat、ノートあなたがその機能をcopypasta場合、それはのようないくつかの他のjQueryの依存関係ありjQuery.isPlainObjectjQuery.isFunction
アンディRaddatz

回答:


131

コードで結果を取得するには、次のようにします。

function extend(a, b){
    for(var key in b)
        if(b.hasOwnProperty(key))
            a[key] = b[key];
    return a;
}

ここで拡張を使用した方法により、デフォルトオブジェクトが変更されることに注意してください。それを望まない場合は、

$.extend({}, default, config)

jQueryの機能を模倣するより堅牢なソリューションは次のとおりです。

function extend(){
    for(var i=1; i<arguments.length; i++)
        for(var key in arguments[i])
            if(arguments[i].hasOwnProperty(key))
                arguments[0][key] = arguments[i][key];
    return arguments[0];
}

1
単純なjsはとてもクールですが抽象的なので、100万人に感謝します。
thednp

3
これは$ .extendのように再帰しません
ekkis

30

Object.assignを使用できます。

var defaults = {key1: "default1", key2: "default2", key3: "defaults3"};
var config = {key1: "value1"};

var settings = Object.assign({}, defaults, config); // values in config override values in defaults
console.log(settings); // Object {key1: "value1", key2: "default2", key3: "defaults3"}

列挙可能なすべてのプロパティの値を1つ以上のソースオブジェクトからターゲットオブジェクトにコピーし、ターゲットオブジェクトを返します。

Object.assign(target, ...sources)

IEを除くすべてのデスクトップブラウザー(ただしEdgeを含む)で動作します。モバイルサポートが緩和されました。

こちらをご覧くださいhttps : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

ディープコピーについて

ただし、Object.assignには、jQueryのextendメソッドにあるような深いオプションはありません。

注:通常、JSONを使用して同様の効果を得ることができます

var config = {key1: "value1"};
    var defaults = {key1: "default1", key2: "default2", keyDeep: {
        kd1: "default3",
        kd2: "default4"
    }};
    var settings = JSON.parse(JSON.stringify(Object.assign({}, defaults, config))); 
    console.log(settings.keyDeep); // Object {kd1: "default3", kd2: "default4"}

ええ、でもここではデフォルトからkey1を飛び越えて新しいオブジェクトに追加していません。
Ionut Necula 16年

@Anonymous実際には追加されますが、config配列のkey1値によってオーバーライドされます。
2016年

私はそれが起こっていると思いました。だから、結局それを追加するのではありません。
Ionut Necula 16年


4

これは、jQueryの依存関係を排除しようとしているときに思いついたディープコピーを使用した、少し異なるアプローチです。それは主に小さいために設計されているので、期待するすべての機能を備えているとは限りません。ES5と完全に互換性がある必要があります(Object.keysの使用によりIE9 以降):

function extend(obj1, obj2) {
    var keys = Object.keys(obj2);
    for (var i = 0; i < keys.length; i += 1) {
      var val = obj2[keys[i]];
      obj1[keys[i]] = ['string', 'number', 'array', 'boolean'].indexOf(typeof val) === -1 ? extend(obj1[keys[i]] || {}, val) : val;
    }
    return obj1;
  }

5行目が正確に何をしているのか不思議に思うかもしれません... obj2.keyがオブジェクトリテラルの場合(つまり、通常の型ではない場合)、再帰的に拡張を呼び出します。その名前のプロパティがobj1にまだ存在しない場合は、最初に空のオブジェクトに初期化します。それ以外の場合は、単にobj1.keyをobj2.keyに設定します。

ここでは、ここで機能する一般的なケースを証明する必要がある私のモカ/チャイテストの一部を示します。

it('should extend a given flat object with another flat object', () => {
  const obj1 = {
    prop1: 'val1',
    prop2: 42,
    prop3: true,
    prop4: 20.16,
  };
  const obj2 = {
    prop4: 77.123,
    propNew1: 'newVal1',
    propNew2: 71,
  };
  assert.deepEqual(utils.extend(obj1, obj2), {
    prop1: 'val1',
    prop2: 42,
    prop3: true,
    prop4: 77.123,
    propNew1: 'newVal1',
    propNew2: 71,
  });
});

it('should deep-extend a given flat object with a nested object', () => {
  const obj1 = {
    prop1: 'val1',
    prop2: 'val2',
  };
  const obj2 = {
    propNew1: 'newVal1',
    propNew2: {
      propNewDeep1: 'newDeepVal1',
      propNewDeep2: 42,
      propNewDeep3: true,
      propNewDeep4: 20.16,
    },
  };
  assert.deepEqual(utils.extend(obj1, obj2), {
    prop1: 'val1',
    prop2: 'val2',
    propNew1: 'newVal1',
    propNew2: {
      propNewDeep1: 'newDeepVal1',
      propNewDeep2: 42,
      propNewDeep3: true,
      propNewDeep4: 20.16,
    },
  });
});

it('should deep-extend a given nested object with another nested object and deep-overwrite members', () => {
  const obj1 = {
    prop1: 'val1',
    prop2: {
      propDeep1: 'deepVal1',
      propDeep2: 42,
      propDeep3: true,
      propDeep4: {
        propDeeper1: 'deeperVal1',
        propDeeper2: 777,
        propDeeper3: 'I will survive',
      },
    },
    prop3: 'lone survivor',
  };
  const obj2 = {
    prop1: 'newVal1',
    prop2: {
      propDeep1: 'newDeepVal1',
      propDeep2: 84,
      propDeep3: false,
      propDeep4: {
        propDeeper1: 'newDeeperVal1',
        propDeeper2: 888,
      },
    },
  };
  assert.deepEqual(utils.extend(obj1, obj2), {
    prop1: 'newVal1',
    prop2: {
      propDeep1: 'newDeepVal1',
      propDeep2: 84,
      propDeep3: false,
      propDeep4: {
        propDeeper1: 'newDeeperVal1',
        propDeeper2: 888,
        propDeeper3: 'I will survive',
      },
    },
    prop3: 'lone survivor',
  });
});

この実装についてのフィードバックやコメントをお待ちしています。前もって感謝します!


2

forステートメントを使用してオブジェクトのプロパティをループできます。

var settings = extend(default, config);

function extend(a, b){
    var c = {};
    for(var p in a)
        c[p] = (b[p] == null) ? a[p] : b[p];
    return c;
}

2
これには、に存在しないプロパティは考慮されませんa。例にはありませんが、$.extend実際にはそのように機能し、すべてのオブジェクトのすべてのプロパティをマージします。
Felix Kling

1
私がjqueryの基本コードを読んだとき、それは実際に値をコピーまたは複製するために再帰的な機能を実行します。したがって、上記のコードのように最初のレベルでコピーするだけではなく、深くクローン/コピーします。
アラジン2014年

2

私のジェネリックなforEachInを使用し、最初のオブジェクトを壊さないこのコードを好みます。

function forEachIn(obj, fn) {
    var index = 0;
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            fn(obj[key], key, index++);
        }
    }
}

function extend() {
    var result = {};
    for (var i = 0; i < arguments.length; i++) {
        forEachIn(arguments[i],
            function(obj, key) {
                result[key] = obj;
            });
    }
    return result;
}

本当に最初のものにものをマージしたい場合は、次のようにすることができます:

obj1 = extend(obj1, obj2);

これが断然最良の答えです。それはディープコピーを行い、最初のオブジェクトを壊さず、無限オブジェクトをサポートし、拡張中に型を変更できます(つまり、ここでの多くの答えはオブジェクトに文字列を拡張しませんが、この答えはそうします)。これは私の本のすべてのチェックマークを取得します。完全拡張置換であり、非常に理解しやすい。
Nick Steele

0

純粋なjavascriptを使用して開発する場合、非常に役立ちます。

function extends(defaults, selfConfig){
     selfConfig = JSON.parse(JSON.stringify(defaults));
     for (var item in config) {
         if (config.hasOwnProperty(item)) {
             selfConfig[item] = config[item];
         }
     }
     return selfConfig;
}

0

Ivan Kuckirのアプローチは、新しいオブジェクトのプロトタイプを作成するために適応させることもできます。

Object.prototype.extend = function(b){
for(var key in b)
    if(b.hasOwnProperty(key))
        this[key] = b[key];
    return this;
}

var settings = default.extend(config);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.