JavaScript-эквивалент метода расширения jQuery
фон
у меня есть функция, которая принимает config
объект в качестве аргумента. Внутри функции у меня также есть . Каждый из этих объектов содержит свойства, которые по существу работают как настройки для остальной части кода внутри функции. Чтобы избежать необходимости указывать все настройки в config
объект, я использую jQuery extend
метод заполнения нового объекта,settings
С любыми значениями по умолчанию от default
объект, если они не были указано в :
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. Есть ли столь же элегантный (или близкий) способ сделать это с помощью простого javascript?
Изменить: Не Дублировать Обоснование
этот вопрос не является дубликатом "как я могу объединить свойства двух объектов JavaScript динамически?" вопрос. Тогда как этот вопрос просто хочет создать объект, содержащий все ключи и значения из двух отдельных объектов - я специально хочу решить, как это сделать в случае, если оба объекта разделяют некоторые, но не все ключи, и какой объект получит приоритет (по умолчанию) для результирующего объекта в случае наличия дубликатов ключей. И даже более конкретно, я хотел бы рассмотреть использование метода jQuery для достижения этого и найти альтернативный способ сделать это без jQuery. Хотя многие ответы на оба вопросы перекрываются, что не означает, что сами вопросы одинаковы.
8 ответов:
чтобы получить результат в коде, вы должны сделать:
function extend(a, b){ for(var key in b) if(b.hasOwnProperty(key)) a[key] = b[key]; return a; }
имейте в виду, что способ, которым вы использовали extend, изменит объект по умолчанию. Если вы этого не хотите, используйте
$.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]; }
вы можете использовать объект.назначить.
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"}
он копирует значения всех перечислимых свойств из одного или нескольких исходных объектов в целевой объект и возвращает целевой объект.
Object.assign(target, ...sources)
Он работает во всех настольных браузерах, кроме IE (но включая Edge). Он смягчил мобильную поддержку.
смотрите сами здесь: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
о глубокой копией
Объект.assign не имеет глубокой опции, которую имеет метод расширения jQuery.Примечание: Вы можете использовать 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"}
вы можете использовать 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};
вы можете перебирать свойства объекта с помощью
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; }
Это мой немного другой подход с глубокой копией, который я придумал, пытаясь устранить зависимость jQuery. Он в основном предназначен для того, чтобы быть маленьким, поэтому он может иметь не все функции, которые можно ожидать. Должен быть полностью совместим с ES5 (начиная с 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; }
вы можете задаться вопросом ,что именно делает пятая строка... Если obj2.ключ-это литерал объекта (т. е. если это не обычный тип), мы рекурсивно вызываем extend на нем. Если свойство с таким именем еще не существует в obj1, мы сначала инициализируем его пустым объектом. В противном случае мы просто ставим obj1.ключ к obj2.ключ.
вот некоторые из моих тестов мокко / чай, которые должны доказать, что общие случаи работают здесь:
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', }); });
Я был бы рад получить обратную связь или комментарии по этой реализации. Заранее спасибо!
Я предпочитаю этот код, который использует мой общий 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);
приближение Ивана Кукира также может быть адаптирована для создания нового прототипа объекта:
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);