JavaScript-эквивалент метода расширения jQuery
Задний план
У меня есть функция, которая принимает объект config
в качестве аргумента. Внутри функции у меня также есть объект по default
. Каждый из этих объектов содержит свойства, которые по существу работают как настройки для остальной части кода внутри функции. Чтобы не указывать все параметры в объекте config
, я использую метод extend
jQuery для заполнения нового объекта, settings
с любыми значениями по default
объекта по 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. Хотя многие ответы на оба вопроса накладываются друг на друга, это не означает, что сами вопросы одинаковы.
Ответы
Ответ 1
Чтобы получить результат в коде, выполните следующие действия:
function extend(a, b){
for(var key in b)
if(b.hasOwnProperty(key))
a[key] = b[key];
return a;
}
Имейте в виду, что способ, которым вы использовали расширение, изменит объект по умолчанию. Если вы этого не хотите, используйте
$.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];
}
Ответ 2
Вы можете использовать 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"}
Ответ 3
Вы можете использовать оператор распространения 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 для старых браузеров
Ответ 4
Вы можете прокручивать объекты Object с помощью инструкции 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;
}
Ответ 5
Это мой немного другой подход с глубокой копией, с которой я столкнулся, пытаясь устранить зависимость 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 - это объектный литерал (т.е. если он не является обычным типом), мы рекурсивно называем его продолжением. Если свойство с этим именем еще не существует в obj1, мы сначала инициализируем его до пустого объекта. В противном случае мы просто устанавливаем obj1.key в obj2.key.
Вот некоторые из моих тестов мокко/чей, которые должны доказать, что обычные случаи работают здесь:
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',
});
});
Я был бы рад обратной связи или комментариям по этой реализации. Спасибо заранее!
Ответ 6
Я предпочитаю этот код, который использует мой generic 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);
Ответ 7
Мне очень помогает, когда я разрабатываю с чистым 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;
}
Ответ 8
Подход Ивана Кукира может быть также адаптирован для создания нового прототипа объекта:
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);