Условные свойства объектов в JavaScript: полное руководство
Узнайте идиоматические способы условного добавления свойств к объектам JavaScript без значений undefined. Изучите операторы spread, тернарные выражения и лучшие практики для создания чистых объектов.
Как условно добавлять свойства в JavaScript объект без включения значений undefined?
Я ищу более идиоматичный способ условного добавления свойств в JavaScript объект. Прямой подход, который я сейчас использую, это:
var a = {};
if (someCondition)
a.b = 5;
Однако я предпочитаю более лаконичное решение. Я попытался использовать тернарный оператор:
a = {
b: (someCondition ? 5 : undefined)
};
Но этот подход добавляет свойство b в объект со значением undefined, что не является желаемым поведением.
Существует ли чистый, идиоматичный способ условного добавления свойств в объект в JavaScript, который позволяет избежать включения свойств со значениями 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)
};
Как можно рефакторить это, чтобы включать только те свойства, которые удовлетворяют своим соответствующим условиям?
Введение
Наиболее идиоматичный способ условного добавления свойств к объекту JavaScript без включения неопределенных значений (undefined) — использование оператора расширения (spread operator) (...) с логическими операторами или тернарными выражениями для условного расширения объектов, а не свойств со значениями undefined.
Содержание
- Оператор расширения с логическим И
- Тернарный оператор с пустыми объектами
- Работа с несколькими свойствами
- Подход с фильтрацией
- Лучшие практики и производительность
- Совместимость с браузерами
Оператор расширения с логическим И
Наиболее лаконичный и идиоматичный подход использует оператор расширения с логическим оператором И (&&):
const a = {
...someCondition && { b: 5 }
};
Как это работает:
- Когда
someConditionистинно (truthy), выражение оценивается как{ b: 5 }, которое расширяется в объект - Когда
someConditionложно (falsy), выражение оценивается какfalse, и расширениеfalseне добавляет никаких свойств в объект
Этот подход особенно элегантен, потому что он естественно “коротко замыкается” (short circuits) — если первый операнд ложен, второй операнд не вычисляется, а false расширяется до ничего.
Согласно Mozilla Developer Network, “случай, когда условие ложно, соответствует пустому объекту, поэтому ничего не добавляется в конечный объект”.
Тернарный оператор с пустыми объектами
Для более сложных условий можно использовать тернарные операторы:
const a = {
...(someCondition ? { b: 5 } : {})
};
Преимущества:
- Более явное представление условной логики
- Легче читается для сложных условий
- Работает с любым типом условий, а не только с проверками на истинность/ложность
Как объясняет Сэм Линдстром, “оператор расширения игнорирует значения без свойства, поэтому age и hairColor — это единственные свойства, применяемые к updateUserObj”.
Работа с несколькими свойствами
Для сценария с несколькими свойствами можно объединить эти подходы:
const a = {
conditionB && { b: 5 },
conditionC && { c: 5 },
conditionD && { d: 5 },
conditionE && { e: 5 },
conditionF && { f: 5 },
conditionG && { g: 5 }
};
Лучший подход с использованием расширения объекта:
const a = {
...(conditionB && { b: 5 }),
...(conditionC && { c: 5 }),
...(conditionD && { d: 5 }),
...(conditionE && { e: 5 }),
...(conditionF && { f: 5 }),
...(conditionG && { g: 5 })
};
Использование вспомогательной функции:
function conditionalSpread(conditions) {
return Object.keys(conditions).reduce((acc, key) => {
const [condition, value] = conditions[key];
if (condition) {
acc[key] = value;
}
return acc;
}, {});
}
const a = conditionalSpread({
b: [conditionB, 5],
c: [conditionC, 5],
d: [conditionD, 5],
e: [conditionE, 5],
f: [conditionF, 5],
g: [conditionG, 5]
});
Подход с фильтрацией
Если вы предпочитаете определить все свойства, а затем отфильтровать их, можно объединить создание объекта с фильтрацией:
const a = {
b: 5,
c: 5,
d: 5,
e: 5,
f: 5,
g: 5
};
// Затем фильтруем на основе условий
Object.keys(a).forEach(key => {
if (!eval(`condition${key.toUpperCase()}`)) {
delete a[key];
}
});
Более элегантный подход с фильтрацией:
const conditions = {
conditionB, conditionC, conditionD,
conditionE, conditionF, conditionG
};
const a = Object.fromEntries(
Object.entries({
b: 5, c: 5, d: 5,
e: 5, f: 5, g: 5
}).filter(([key]) => conditions[`condition${key.toUpperCase()}`])
);
Как упоминается в блоге CoreUI, вы можете “объединять несколько условий с логическими операторами для более сложных сценариев.”
Лучшие практики и производительность
Соображения по производительности:
- Подход с использованием оператора расширения обычно быстрее, чем фильтрация
- Подход с логическим И более лаконичен, но может быть менее читаем для сложных условий
- Для больших объектов с множеством условных свойств рассмотрите использование вспомогательных функций
Лучшие практики:
// Хорошо: Четко и читаемо
const user = {
name: 'John',
...hasEmail && { email: 'john@example.com' },
...isAdmin && { role: 'admin' }
};
// Хорошо: Для нескольких связанных условий
const config = {
...featureEnabled && {
setting1: true,
setting2: 'value',
setting3: 42
}
};
Оливер Джем объясняет, что “логический оператор И (
&&) проверяет, является ли левая часть истинной, и если да, оценивается как правая часть. Если левая часть ложна, происходит ‘короткое замыкание’ и правая часть не вычисляется.”
Совместимость с браузерами
Оператор расширения для объектов был добавлен в ES2018 и поддерживается в:
- Chrome 60+
- Firefox 55+
- Safari 11.1+
- Edge 79+
Для более старых браузеров потребуется использовать альтернативные подходы:
// Для совместимости с ES5
var a = {};
if (someCondition) {
a.b = 5;
}
// Или с использованием Object.assign
var a = Object.assign({},
someCondition && { b: 5 }
);
Как отмечено в обсуждении на Stack Overflow, “ES6 обрабатывает значения false, undefined, null и т.д. как {}. Поэтому расширение …{} не добавит никаких свойств в родительский объект.”
Заключение
- Используйте оператор расширения с логическим И для наиболее лаконичного условного добавления свойств
- Тернарный оператор с пустыми объектами обеспечивает лучшую читаемость для сложных условий
- Для нескольких свойств объединяйте несколько операций расширения или используйте вспомогательные функции
- Подход с фильтрацией хорошо работает, когда нужно обрабатывать существующие объекты
- Учитывайте совместимость с браузерами и используйте полифилы или альтернативный синтаксис для старых окружений
Подход с использованием оператора расширения стал стандартным идиоматическим способом обработки условных свойств объектов в современном JavaScript, предлагая одновременно лаконичность и ясность, избегая неопределенных значений в конечном объекте.
Источники
- Spread syntax (…) - JavaScript | MDN
- Conditional Object Properties Using Spread in JavaScript | Medium
- How to Conditionally Add a Property to an Object in JavaScript · CoreUI
- Two ways to conditionally set object properties | oliverjam.com
- Conditionally spreading objects in JavaScript | Amit Merchant
- javascript - Assign, and set js object property conditionally - Stack Overflow
- Conditionally adding entries inside Array and object literals | 2ality