JQuery'nin genişletme yönteminin JavaScript eşdeğeri


94

Arka fon

Bir confignesneyi argüman olarak alan bir fonksiyonum var . Fonksiyonun içinde bir de nesnem var default. Bu nesnelerin her biri, esasen işlev içindeki kodun geri kalanı için ayarlar olarak çalışan özellikleri içerir. İçindeki tüm ayarları belirtmek zorunda önlemek için confignesnenin, ben jQuery'nin kullanmak extend, yeni bir nesnede dolgu yöntemi settingsherhangi varsayılan değerlerle defaultonlar belirtilen olmasaydı nesne confignesne:

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};

Sorun

Bu harika çalışıyor, ancak bu işlevi jQuery'ye ihtiyaç duymadan yeniden oluşturmak istiyorum. Bunu düz javascript ile yapmanın eşit derecede zarif (veya yakın) bir yolu var mı?


Düzenleme: Yinelenmemiş Gerekçe

Bu soru, " İki JavaScript nesnesinin özelliklerini dinamik olarak nasıl birleştirebilirim? " Sorusunun bir kopyası değildir . Oysa bu soru basitçe iki ayrı nesneden tüm anahtarları ve değerleri içeren bir nesne yaratmak isterken - özellikle her iki nesnenin de bazı anahtarları paylaşması ancak tüm anahtarları paylaşmaması ve hangi nesnenin öncelik kazanması durumunda bunun nasıl yapılacağını ele almak istiyorum ( varsayılan) yinelenen anahtarların olması durumunda ortaya çıkan nesne için. Ve daha spesifik olarak, bunu başarmak için jQuery yönteminin kullanımına değinmek ve bunu jQuery olmadan yapmanın alternatif bir yolunu bulmak istedim. Her iki sorunun cevabının çoğu örtüşse de bu, soruların kendilerinin aynı olduğu anlamına gelmez.



İyi fikir @RocketHazmat, bu işlevi kopyalarsanız, jQuery.isPlainObjectve gibi birkaç başka jQuery bağımlılığına sahip olduğuna dikkat edinjQuery.isFunction
Andy Raddatz

Yanıtlar:


134

Sonucu kodunuzda almak için şunları yaparsınız:

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

Uzatmayı kullanma şeklinizin varsayılan nesneyi değiştireceğini unutmayın. Bunu istemiyorsan kullan

$.extend({}, default, config)

JQuery'nin işlevselliğini taklit eden daha sağlam bir çözüm aşağıdaki gibi olacaktır:

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
Bir kod örneğini iş başında görmek istiyorum, belki keman, çünkü düz js çok daha havalı ama çok soyut, bir milyon teşekkürler.
thednp

3
bu, $ .extend'in yaptığı gibi yinelenmez
ekkis

30

Object.assign'ı kullanabilirsiniz.

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"}

Tüm numaralandırılabilir kendi özelliklerinin değerlerini bir veya daha fazla kaynak nesneden bir hedef nesneye kopyalar ve hedef nesneyi döndürür.

Object.assign(target, ...sources)

IE hariç (ancak Edge dahil) tüm masaüstü tarayıcılarda çalışır. Mobil desteği azalttı.

Burada kendiniz görün: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Derin kopya hakkında

Ancak, Object.assign, jQuery'nin genişleme yönteminin sahip olduğu derin seçeneğe sahip değildir.

Not: JSON'u genellikle benzer bir efekt için kullanabilirsiniz.

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"}

Evet, ama burada varsayılanlardan key1'in üzerine atlar ve onu yeni nesneye eklemez.
Ionut Necula

@Anonymous Aslında ekler, ancak yapılandırma dizisindeki key1 değeri tarafından geçersiz kılınır.
ling

Sanırım bu oluyor. Yani, sonradan eklemiyor.
Ionut Necula


5

Bir jQuery bağımlılığını ortadan kaldırmaya çalışırken bulduğum derin kopya ile biraz farklı yaklaşımım bu. Çoğunlukla küçük olması için tasarlanmıştır, bu nedenle beklediğiniz tüm özelliklere sahip olmayabilir. Tamamen ES5 uyumlu olmalıdır ( Object.keys kullanımı nedeniyle IE9'dan başlayarak ):

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;
  }

Beşinci satırın tam olarak ne yaptığını merak edebilirsiniz ... Eğer obj2.key bir nesne değişmezi ise (yani, sıradan bir tür değilse), onun üzerinde yinelemeli olarak expand çağırırız. Bu isimde bir özellik henüz obj1'de mevcut değilse, önce onu boş bir nesneye başlatırız. Aksi takdirde, obj1.key'yi obj2.key olarak ayarlarız.

İşte burada işe yarayan yaygın vakaları kanıtlaması gereken mocha / chai testlerimden bazıları:

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',
  });
});

Bu uygulamayla ilgili geri bildirim veya yorumlardan memnun olurum. Şimdiden teşekkürler!


2

İfadeyi kullanarak Object özelliklerinde döngü yapabilirsiniz 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
Bu, içinde bulunmayan mülkleri hesaba katmaz a. Örnekte olmasa da, $.extendaslında tüm nesnelerin tüm özelliklerini birleştirerek bu şekilde çalışır.
Felix Kling

1
Jquery temel kodunu okurken, aslında değeri kopyalamak veya klonlamak için özyinelemeli bir işlev yapıyor. Yani, sadece birinci seviyede yukarıdaki kod gibi kopyalamak değil, derinlemesine klonlamak / kopyalamaktır.
aladine

2

Genel forEachIn'i kullanan ve ilk nesneyi karıştırmayan bu kodu tercih ederim:

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;
}

Öğeleri ilk nesnede gerçekten birleştirmek istiyorsanız, şunları yapabilirsiniz:

obj1 = extend(obj1, obj2);

Bu, buradaki en iyi cevaptır. Derin kopyalama yapar, ilk nesneyi karıştırmaz, sonsuz nesneleri destekler ve genişleme sırasında türleri değiştirebilir (yani buradaki birçok cevap bir nesneye dizgiyi genişletmez, bu cevap olacaktır). Bu, kitabımdaki her onay işaretini alıyor. tam genişletilmiş değiştirme ve anlaşılması son derece kolay.
Nick Steele

0

Saf javascript ile geliştirdiğimde bana çok yardımcı oluyor.

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'in yaklaşımı, yeni bir nesne prototipi oluşturmak için de uyarlanabilir:

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);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.