JavaScript-equivalent van jQuery’s extend-methode

Ik heb een functie die een config-object als argument neemt. Binnen de functie heb ik ook defaultobject. Elk van die objecten bevat eigenschappen die in wezen werken als instellingen voor de rest van de code binnen de functie. Om te voorkomen dat ik alle instellingen binnen het configobject moet specificeren, gebruik ik jQuery’s extendmethode om een ​​nieuw object in te vullen, settingsmet eventuele standaardwaarden van het default-object als ze niet zijn opgegeven in het config-object:

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

Probleem

Dit werkt prima, maar ik wil deze functionaliteit graag reproduceren zonder dat jQuery nodig is. Is er een even elegante (of bijna) manier om dit te doen met gewoon javascript?


Bewerken: niet-dubbele motivering

Deze vraag is geen duplicaat van de “Hoe kan ik eigenschappen van twee JavaScript-objecten dynamisch samenvoegen?” vraag. Terwijl die vraag eenvoudigweg een object wil maken dat alle sleutels en waarden van twee afzonderlijke objecten bevat – ik wil specifiek bespreken hoe dit te doen in het geval dat beide objecten enkele maar niet alle sleutels delen en welk object voorrang krijgt ( de standaard) voor het resulterende object in het geval dat er dubbele sleutels zijn. En nog specifieker, ik wilde ingaan op het gebruik van de methode van jQuery om dit te bereiken en een alternatieve manier vinden om dit te doen zonder jQuery. Hoewel veel van de antwoorden op beide vragen elkaar overlappen, betekent dat niet dat de vragen zelf hetzelfde zijn.


Antwoord 1, autoriteit 100%

Om het resultaat in uw code te krijgen, doet u het volgende:

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

Houd er rekening mee dat de manier waarop u extend daar gebruikt, het standaardobject zal wijzigen. Als je dat niet wilt, gebruik dan

$.extend({}, default, config)

Een robuustere oplossing die de functionaliteit van jQuery nabootst, zou als volgt zijn:

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

Antwoord 2, autoriteit 33%

U kunt de ECMA 2018 spread-operator in object gebruiken letterlijke

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

BabelJS-ondersteuning voor oudere browsers


Antwoord 3, autoriteit 24%

U kunt Object.assign gebruiken.

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

Het kopieert de waarden van alle traaglijke eigen eigenschappen van een of meer bronobjecten naar een doelobject en retourneert het doelobject.

Object.assign(target, ...sources)

Het werkt in alle desktopbrowsers behalve IE (maar inclusief EDGE).
Het heeft mobiele ondersteuning beperkend.

Zie hier zelf:
https://developer.mozilla.org/en-us/docs/ Web / Javascript / Referentie / Global_Objects / Object / Assign

Over Deep Copy

Object.Assign heeft echter niet de diepe optie die de uitstrekkende methode van JQuery heeft.

Opmerking: u kunt JOSS over het algemeen gebruiken voor een soortgelijk effect

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

Antwoord 4, Autoriteit 5%

Dit is mijn enigszins andere benadering met diepe kopie waar ik bij kwam terwijl ik probeerde een jQuery-afhankelijkheid te elimineren. Het is meestal ontworpen om klein te zijn, dus het heeft misschien niet alle beschikken over één verwacht. Moet volledig ES5-compatibel zijn (vanaf IE9 vanwege het gebruik van Object.Keys ):

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

U kunt zich afvragen wat de vijfde regel precies doet … Als OBJ2.Key een objectletterig is (d.w.z. als het geen gewoon type is), bellen we ons op. Als een eigenschap met die naam nog niet bestaat in OBJ1, initialiseren we het eerst naar een leeg object. Anders stellen we Simpelweg OBJ1.key in op OBJ2.Key.

Hier zijn enkele van mijn Mocha / Chai-tests die de gemeenschappelijke zaken moeten bewijzen om hier te werken:

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

Ik zou graag feedback of opmerkingen over deze implementatie ontvangen. Bij voorbaat dank!


Antwoord 5

Je kunt door de eigenschappen van Object lopen met de instructie 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;
}

Antwoord 6

Ik geef de voorkeur aan deze code die mijn generieke forEachIn gebruikt en het eerste object niet verminkt:

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

Als je echt dingen in het eerste object wilt samenvoegen, kun je het volgende doen:

obj1 = extend(obj1, obj2);

Antwoord 7

Het helpt me enorm als ik ontwikkel met puur 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;
}

Antwoord 8

De aanpak van Ivan Kuckir’skan ook worden aangepast om een ​​nieuw object-prototype te maken:

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

Other episodes