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