Как в JavaScript условно добавить член к объекту?
Я хотел бы создать объект с условно добавляемыми свойствами. Простой подход выглядит так:
var a = {};
if (someCondition)
a.b = 5;
Теперь я хотел бы написать более идиоматичный код. Я пытаюсь сделать так:
a = {
b: (someCondition ? 5 : undefined)
};
Но теперь свойство b
является членом объекта a
и его значение равно undefined
. Это не то, что я хочу получить.
Есть ли удобное решение этой проблемы?
Дополнение
Я ищу решение, которое сможет обрабатывать общий случай с несколькими свойствами.
a = {
b: (conditionB ? 5 : undefined),
c: (conditionC ? 5 : undefined),
d: (conditionD ? 5 : undefined),
e: (conditionE ? 5 : undefined),
f: (conditionF ? 5 : undefined),
g: (conditionG ? 5 : undefined),
};
5 ответ(ов)
Я думаю, что @InspiredJW сделал это с помощью ES5, и, как указал @trincot, использование ES6 — более предпочтительный подход. Но мы можем добавить немного «сахара», используя оператор распространения и короткое замыкание логического И:
const a = {
...(someCondition && {b: 5})
}
В приведённом вами коде используется конструкция с оператором распространения (spread operator) в сочетании с логическим выражением. Давайте разберёмся, как это работает.
Код создаёт объект obj
, в который добавляются некоторые свойства в зависимости от условия.
const obj = {
...(condition) && {someprop: propvalue},
...otherprops
}
Если условие condition
истинно (true), то выражение ...(condition) && {someprop: propvalue}
возвращает объект {someprop: propvalue}
, и его свойства добавляются в создаваемый объект. Если condition
ложно (false), то результатом будет false
, который не является объектом, и, следовательно, ничего не добавится.
Теперь давайте рассмотрим ваш пример с Live Demo
:
const obj = {
...(true) && {someprop: 42}, // добавит someprop: 42
...(false) && {nonprop: "foo"}, // не добавит ничего
...({}) && {tricky: "hello"}, // что-то не сработает здесь
}
console.log(obj);
Результат будет следующем:
...(true) && {someprop: 42}
— это условие истинно, следовательно, в объектobj
будет добавлено свойствоsomeprop
со значением42
....(false) && {nonprop: "foo"}
— это условие ложно, поэтому ничего не добавляется....({}) && {tricky: "hello"}
— здесь{}
(пустой объект) превращается вtrue
, но так как оператор&&
ожидает вернуть значение, а не добавлять его в объект, это выражение не будет работать как задумано, фактически внутриspread
оно будет просто игнорироваться.
Таким образом, результатом выполнения console.log(obj);
будет:
{ someprop: 42 }
Если вы хотите добавить свойство только если условие выполняется, правильным способом будет использовать тернарный оператор или проверку условий перед добавлением свойства. Например:
const obj = {
...(condition ? {someprop: propvalue} : {}),
...otherprops
}
Надеюсь, это поможет вам лучше понять, как использовать оператор распространения в JavaScript!
Я предлагаю следующий вариант кода:
const a = {
...(someCondition ? {b: 5} : {})
}
Этот код использует оператор распространения (spread operator) для создания объекта a
. Если someCondition
истинно, в объект будет добавлено свойство b
со значением 5. Если условие ложно, в объект добавляться ничего не будет, и он останется пустым. Это полезный способ динамически формировать объекты в зависимости от условий.
Тест производительности
Классический подход:
const a = {};
if (someCondition)
a.b = 5;
VS
Подход со спред-оператором:
const a2 = {
...(someCondition && {b: 5})
}
Результаты:
Классический подход показывает гораздо большую скорость, поэтому стоит учитывать, что синтаксический сахар медленнее.
testClassicConditionFulfilled(); // ~ 234.9ms
testClassicConditionNotFulfilled(); // ~493.1ms
testSpreadOperatorConditionFulfilled(); // ~2649.4ms
testSpreadOperatorConditionNotFulfilled(); // ~2278.0ms
function testSpreadOperatorConditionFulfilled() {
const value = 5;
console.time('testSpreadOperatorConditionFulfilled');
for (let i = 0; i < 200000000; i++) {
let a = {
...(value && {b: value})
};
}
console.timeEnd('testSpreadOperatorConditionFulfilled');
}
function testSpreadOperatorConditionNotFulfilled() {
const value = undefined;
console.time('testSpreadOperatorConditionNotFulfilled');
for (let i = 0; i < 200000000; i++) {
let a = {
...(value && {b: value})
};
}
console.timeEnd('testSpreadOperatorConditionNotFulfilled');
}
function testClassicConditionFulfilled() {
const value = 5;
console.time('testClassicConditionFulfilled');
for (let i = 0; i < 200000000; i++) {
let a = {};
if (value)
a.b = value;
}
console.timeEnd('testClassicConditionFulfilled');
}
function testClassicConditionNotFulfilled() {
const value = undefined;
console.time('testClassicConditionNotFulfilled');
for (let i = 0; i < 200000000; i++) {
let a = {};
if (value)
a.b = value;
}
console.timeEnd('testClassicConditionNotFulfilled');
}
testClassicConditionFulfilled(); // ~ 234.9ms
testClassicConditionNotFulfilled(); // ~493.1ms
testSpreadOperatorConditionFulfilled(); // ~2649.4ms
testSpreadOperatorConditionNotFulfilled(); // ~2278.0ms
Вывод: Если вам важна производительность, лучше использовать классический способ и избегать использования спред-оператора в условиях, когда эффективность кода имеет первостепенное значение.
Использование расширенных свойств объектов действительно является хорошим решением, особенно если вам нужно добавлять свойства в объект или элементы в массив только при выполнении определённых условий. Пример с использованием [isConditionTrue() && 'propertyName']: 'propertyValue'
работает, но может быть менее очевидным и несколько запутанным.
Согласно обновлению, подход, предложенный Акселем Раушмейером в его блоге, более предпочтителен. Он использует оператор расширения (...
) для добавления элементов в массив или свойства в объект, если условие выполняется. Примеры выглядят следующим образом:
const arr = [
...(isConditionTrue() ? [{
key: 'value'
}] : [])
];
const obj = {
...(isConditionTrue() ? {key: 'value'} : {})
};
Этот подход более читаем и легко понимается. Он также не создает пустых свойств, если условие не выполняется, что позволяет избежать ненужных значений в вашем объекте или массиве. Это решение может значительно упростить ваш код и сделать его более чистым. Спасибо за идею!
Где найти документацию по форматированию даты в JavaScript?
В чем разница между String.slice и String.substring?
Проверка соответствия строки регулярному выражению в JS
Существует ли ссылка на "последнюю" библиотеку jQuery в Google APIs?
Как создать диалог с кнопками "Ок" и "Отмена"