Equivalente JavaScript del metodo di estensione di jQuery

sfondo

Ho una funzione che accetta un object di config come argomento. All’interno della funzione, ho anche un object default . Ognuno di questi oggetti contiene proprietà che funzionano essenzialmente come impostazioni per il resto del codice all’interno della funzione. Per evitare di dover specificare tutte le impostazioni all’interno dell’object config , utilizzo il metodo di extend di jQuery per compilare un nuovo object, le settings con qualsiasi valore default dall’object default se non sono state specificate nell’object 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}; 

Problema

Funziona alla grande, ma mi piacerebbe riprodurre questa funzionalità senza la necessità di jQuery. C’è un altrettanto elegante (o vicino) per fare questo con plain ol ‘javascript?


Modifica: giustificazione non duplicata

Questa domanda non è un duplicato della domanda ” Come posso unire proprietà di due oggetti JavaScript in modo dinamico? “. Mentre quella domanda vuole semplicemente creare un object che contenga tutte le chiavi e i valori di due oggetti separati: voglio specificatamente indirizzare come farlo nel caso in cui entrambi gli oggetti condividano alcune ma non tutte le chiavi e quale object avrà la precedenza ( il valore predefinito) per l’object risultante nel caso in cui vi siano chiavi duplicate. E ancor più specificamente, volevo affrontare l’uso del metodo di jQuery per raggiungere questo objective e trovare un modo alternativo per farlo senza jQuery. Mentre molte delle risposte a entrambe le domande si sovrappongono, ciò non significa che le domande stesse siano le stesse.

Per ottenere il risultato nel tuo codice, dovresti fare:

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

Tieni presente che il modo in cui hai utilizzato Esteso modificherà l’object predefinito. Se non lo vuoi, usa

 $.extend({}, default, config) 

Una soluzione più solida che imita la funzionalità di jQuery sarebbe la seguente:

 function extend(){ for(var i=1; i 

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

Copia i valori di tutte le proprietà enumerabili da uno o più oggetti di origine su un object di destinazione e restituisce l’object di destinazione.

 Object.assign(target, ...sources) 

Funziona su tutti i browser desktop tranne IE (ma incluso Edge). Ha attenuato il supporto mobile.

Vedi qui: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Informazioni sulla copia profonda

Tuttavia, Object.assign non ha l’opzione profonda che ha il metodo di estensione di jQuery.

Nota: generalmente puoi usare JSON per un effetto simile

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

Puoi utilizzare l’ operatore di spread ECMA 2015 …

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

Supporto BabelJS per i browser più vecchi

È ansible eseguire il looping delle proprietà dell’object utilizzando for dichiarazione.

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

Questo è il mio approccio leggermente diverso con la copia profonda che ho trovato mentre cercavo di eliminare una dipendenza da jQuery. È in gran parte progettato per essere piccolo, quindi potrebbe non avere tutte le funzionalità che ci si aspetta. Dovrebbe essere completamente compatibile con ES5 (a partire da IE9 a causa dell’utilizzo di 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; } 

Potresti chiederti che cosa fa esattamente la quinta riga ... Se obj2.key è un object letterale (cioè se non è un tipo ordinario) chiamiamo ricorsivamente l'estensione su di esso. Se una proprietà con quel nome non esiste ancora in obj1, inizializziamo prima l'object vuoto. Altrimenti, semplicemente impostiamo obj1.key su obj2.key.

Ecco alcuni dei miei test moka / chai che dovrebbero dimostrare che i casi più comuni funzionano qui:

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

Sarei felice di feedback o commenti su questa implementazione. Grazie in anticipo!

Preferisco questo codice che utilizza il mio forEachIn generico e non manipola il primo object:

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

Se vuoi veramente unire le cose nel primo object, puoi fare:

 obj1 = extend(obj1, obj2); 

L’approccio di Ivan Kuckir può anche essere adattato per creare un nuovo prototipo di object:

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

Mi aiuta molto quando sviluppo con puro 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; }