11

Как в JavaScript условно добавить член к объекту?

18

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

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 ответ(ов)

23

Я думаю, что @InspiredJW сделал это с помощью ES5, и, как указал @trincot, использование ES6 — более предпочтительный подход. Но мы можем добавить немного «сахара», используя оператор распространения и короткое замыкание логического И:

const a = {
   ...(someCondition && {b: 5})
}
3

В приведённом вами коде используется конструкция с оператором распространения (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);

Результат будет следующем:

  1. ...(true) && {someprop: 42} — это условие истинно, следовательно, в объект obj будет добавлено свойство someprop со значением 42.
  2. ...(false) && {nonprop: "foo"} — это условие ложно, поэтому ничего не добавляется.
  3. ...({}) && {tricky: "hello"} — здесь {} (пустой объект) превращается в true, но так как оператор && ожидает вернуть значение, а не добавлять его в объект, это выражение не будет работать как задумано, фактически внутри spread оно будет просто игнорироваться.

Таким образом, результатом выполнения console.log(obj); будет:

{ someprop: 42 }

Если вы хотите добавить свойство только если условие выполняется, правильным способом будет использовать тернарный оператор или проверку условий перед добавлением свойства. Например:

const obj = {
  ...(condition ? {someprop: propvalue} : {}),
  ...otherprops
}

Надеюсь, это поможет вам лучше понять, как использовать оператор распространения в JavaScript!

1

Я предлагаю следующий вариант кода:

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

Этот код использует оператор распространения (spread operator) для создания объекта a. Если someCondition истинно, в объект будет добавлено свойство b со значением 5. Если условие ложно, в объект добавляться ничего не будет, и он останется пустым. Это полезный способ динамически формировать объекты в зависимости от условий.

0

Тест производительности

Классический подход:

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

Вывод: Если вам важна производительность, лучше использовать классический способ и избегать использования спред-оператора в условиях, когда эффективность кода имеет первостепенное значение.

0

Использование расширенных свойств объектов действительно является хорошим решением, особенно если вам нужно добавлять свойства в объект или элементы в массив только при выполнении определённых условий. Пример с использованием [isConditionTrue() && 'propertyName']: 'propertyValue' работает, но может быть менее очевидным и несколько запутанным.

Согласно обновлению, подход, предложенный Акселем Раушмейером в его блоге, более предпочтителен. Он использует оператор расширения (...) для добавления элементов в массив или свойства в объект, если условие выполняется. Примеры выглядят следующим образом:

const arr = [
  ...(isConditionTrue() ? [{
    key: 'value'
  }] : [])
];

const obj = {
  ...(isConditionTrue() ? {key: 'value'} : {})
};

Этот подход более читаем и легко понимается. Он также не создает пустых свойств, если условие не выполняется, что позволяет избежать ненужных значений в вашем объекте или массиве. Это решение может значительно упростить ваш код и сделать его более чистым. Спасибо за идею!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь