Другое

Условные свойства объектов в JavaScript: полное руководство

Узнайте идиоматические способы условного добавления свойств к объектам JavaScript без значений undefined. Изучите операторы spread, тернарные выражения и лучшие практики для создания чистых объектов.

Как условно добавлять свойства в JavaScript объект без включения значений undefined?

Я ищу более идиоматичный способ условного добавления свойств в JavaScript объект. Прямой подход, который я сейчас использую, это:

javascript
var a = {};
if (someCondition)
    a.b = 5;

Однако я предпочитаю более лаконичное решение. Я попытался использовать тернарный оператор:

javascript
a = {
    b: (someCondition ? 5 : undefined)
};

Но этот подход добавляет свойство b в объект со значением undefined, что не является желаемым поведением.

Существует ли чистый, идиоматичный способ условного добавления свойств в объект в JavaScript, который позволяет избежать включения свойств со значениями undefined?

Обновление: Я также ищу решение, которое может обрабатывать общий случай с несколькими свойствами:

javascript
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.

Содержание


Оператор расширения с логическим И

Наиболее лаконичный и идиоматичный подход использует оператор расширения с логическим оператором И (&&):

javascript
const a = {
  ...someCondition && { b: 5 }
};

Как это работает:

  • Когда someCondition истинно (truthy), выражение оценивается как { b: 5 }, которое расширяется в объект
  • Когда someCondition ложно (falsy), выражение оценивается как false, и расширение false не добавляет никаких свойств в объект

Этот подход особенно элегантен, потому что он естественно “коротко замыкается” (short circuits) — если первый операнд ложен, второй операнд не вычисляется, а false расширяется до ничего.

Согласно Mozilla Developer Network, “случай, когда условие ложно, соответствует пустому объекту, поэтому ничего не добавляется в конечный объект”.

Тернарный оператор с пустыми объектами

Для более сложных условий можно использовать тернарные операторы:

javascript
const a = {
  ...(someCondition ? { b: 5 } : {})
};

Преимущества:

  • Более явное представление условной логики
  • Легче читается для сложных условий
  • Работает с любым типом условий, а не только с проверками на истинность/ложность

Как объясняет Сэм Линдстром, “оператор расширения игнорирует значения без свойства, поэтому age и hairColor — это единственные свойства, применяемые к updateUserObj”.


Работа с несколькими свойствами

Для сценария с несколькими свойствами можно объединить эти подходы:

javascript
const a = {
  conditionB && { b: 5 },
  conditionC && { c: 5 },
  conditionD && { d: 5 },
  conditionE && { e: 5 },
  conditionF && { f: 5 },
  conditionG && { g: 5 }
};

Лучший подход с использованием расширения объекта:

javascript
const a = {
  ...(conditionB && { b: 5 }),
  ...(conditionC && { c: 5 }),
  ...(conditionD && { d: 5 }),
  ...(conditionE && { e: 5 }),
  ...(conditionF && { f: 5 }),
  ...(conditionG && { g: 5 })
};

Использование вспомогательной функции:

javascript
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]
});

Подход с фильтрацией

Если вы предпочитаете определить все свойства, а затем отфильтровать их, можно объединить создание объекта с фильтрацией:

javascript
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];
  }
});

Более элегантный подход с фильтрацией:

javascript
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, вы можете “объединять несколько условий с логическими операторами для более сложных сценариев.”


Лучшие практики и производительность

Соображения по производительности:

  • Подход с использованием оператора расширения обычно быстрее, чем фильтрация
  • Подход с логическим И более лаконичен, но может быть менее читаем для сложных условий
  • Для больших объектов с множеством условных свойств рассмотрите использование вспомогательных функций

Лучшие практики:

javascript
// Хорошо: Четко и читаемо
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+

Для более старых браузеров потребуется использовать альтернативные подходы:

javascript
// Для совместимости с ES5
var a = {};
if (someCondition) {
  a.b = 5;
}

// Или с использованием Object.assign
var a = Object.assign({}, 
  someCondition && { b: 5 }
);

Как отмечено в обсуждении на Stack Overflow, “ES6 обрабатывает значения false, undefined, null и т.д. как {}. Поэтому расширение …{} не добавит никаких свойств в родительский объект.”

Заключение

  1. Используйте оператор расширения с логическим И для наиболее лаконичного условного добавления свойств
  2. Тернарный оператор с пустыми объектами обеспечивает лучшую читаемость для сложных условий
  3. Для нескольких свойств объединяйте несколько операций расширения или используйте вспомогательные функции
  4. Подход с фильтрацией хорошо работает, когда нужно обрабатывать существующие объекты
  5. Учитывайте совместимость с браузерами и используйте полифилы или альтернативный синтаксис для старых окружений

Подход с использованием оператора расширения стал стандартным идиоматическим способом обработки условных свойств объектов в современном JavaScript, предлагая одновременно лаконичность и ясность, избегая неопределенных значений в конечном объекте.

Источники

  1. Spread syntax (…) - JavaScript | MDN
  2. Conditional Object Properties Using Spread in JavaScript | Medium
  3. How to Conditionally Add a Property to an Object in JavaScript · CoreUI
  4. Two ways to conditionally set object properties | oliverjam.com
  5. Conditionally spreading objects in JavaScript | Amit Merchant
  6. javascript - Assign, and set js object property conditionally - Stack Overflow
  7. Conditionally adding entries inside Array and object literals | 2ality
Авторы
Проверено модерацией
Модерация