Распространение объекта против Object.assign

Допустим, у меня есть переменная options и я хочу установить значение по умолчанию.

Какова польза/недостаток этих двух альтернатив?

Использование объекта распространения

options = {...optionsDefault, ...options};

Или используя Object.assign

options = Object.assign({}, optionsDefault, options);

Это обязательство, что заставило меня задуматься.

Ответ 1

Это не обязательно является исчерпывающим.

Синтаксис распространения

options = {...optionsDefault, ...options};

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

  • Если вы создаете код для выполнения в средах без встроенной поддержки, вы можете просто скомпилировать этот синтаксис (в отличие от использования полифилла). (С Вавилоном, например.)

  • Менее многословно.

Недостатки:

  • Когда этот ответ был изначально написан, это было предложение, не стандартизированное. При использовании предложений учитывайте, что вы будете делать, если вы сейчас пишете код с ним, и он не стандартизируется и не изменяется по мере продвижения к стандартизации. С тех пор это было стандартизировано в ES2018.

  • Буквально, не динамично.


Object.assign()

options = Object.assign({}, optionsDefault, options);

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

  • Стандартизован.

  • Динамический. Пример:

    var sources = [{a: "A"}, {b: "B"}, {c: "C"}];
    options = Object.assign.apply(Object, [{}].concat(sources));
    // or
    options = Object.assign({}, ...sources);
    

Недостатки:

  • Более многословно.
  • Если вы разрабатываете код для выполнения в средах без встроенной поддержки, вам необходимо выполнить многократное заполнение.

Это то, что заставило меня задуматься.

Это не имеет прямого отношения к тому, что вы спрашиваете. Этот код не использовал Object.assign(), он использовал код пользователя (object-assign), который делает то же самое. Похоже, они компилируют этот код с Babel (и связывают его с Webpack), о чем я говорил: синтаксис, который вы можете просто скомпилировать. По-видимому, они предпочли включить object-assign в качестве зависимости, которая войдет в их сборку.

Ответ 2

Для эталонного объекта отдых/разворот завершается в ECMAScript 2018 как этап 4. Предложение можно найти здесь.

В большинстве случаев сброс и распространение объектов работают одинаково, ключевое отличие состоит в том, что распространение определяет свойства, а Object.assign() устанавливает их. Это означает, что Object.assign() запускает сеттеры.

Стоит помнить, что, кроме этого, объект rest/spread 1:1 отображается на Object.assign() и действует иначе, чем массив (повторяемый). Например, при расширении массива распространяются нулевые значения. Однако использование нулевых значений распространения объекта молча распространяется на ничто.

Пример массива (итерируемого) спреда

const x = [1, 2, null , 3];
const y = [...x, 4, 5];
const z = null;

console.log(y); // [1, 2, null, 3, 4, 5];
console.log([...z]); // TypeError

Пример распространения объекта

const x = null;
const y = {a: 1, b: 2};
const z = {...x, ...y};

console.log(z); //{a: 1, b: 2}

Это согласуется с тем, как будет работать Object.assign(), оба молча исключают нулевое значение без ошибок.

const x = null;
const y = {a: 1, b: 2};
const z = Object.assign({}, x, y);

console.log(z); //{a: 1, b: 2}

Ответ 3

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

const error = new Error();
error instanceof Error // true

const errorExtendedUsingSpread = {
  ...error,
  ...{
    someValue: true
  }
};
errorExtendedUsingSpread instanceof Error; // false

const errorExtendedUsingAssign = Object.assign(error, {
  someValue: true
});
errorExtendedUsingAssign instanceof Error; // true

Ответ 4

Оператор распространения объектов (...) не работает в браузерах, поскольку он пока не является частью какой-либо спецификации ES, а просто представляет собой предложение. Единственный вариант - скомпилировать его с Babel (или что-то подобное).

Как вы можете видеть, это просто синтаксический сахар над Object.assign({}).

Насколько я вижу, это важные различия.

  • Object.assign работает в большинстве браузеров (без компиляции)
  • ... для объектов не стандартизируется
  • ... защищает вас от случайного мутирования объекта
  • ... будет polyfill Object.assign в браузерах без него
  • ... требуется меньше кода для выражения той же идеи

Ответ 5

Как уже упоминали другие, в этот момент написания Object.assign() требует полифилла и распространения объекта ... требует некоторой транспиляции (и, возможно, также полифилла) для работы.

Рассмотрим этот код:

// Babel wont touch this really, it will simply fail if Object.assign() is not supported in browser.
const objAss = { message: 'Hello you!' };
const newObjAss = Object.assign(objAss, { dev: true });
console.log(newObjAss);

// Babel will transpile with use to a helper function that first attempts to use Object.assign() and then falls back.
const objSpread = { message: 'Hello you!' };
const newObjSpread = {...objSpread, dev: true };
console.log(newObjSpread);

Оба они дают одинаковый результат.

Вот вывод из Babel в ES5:

var objAss = { message: 'Hello you!' };
var newObjAss = Object.assign(objAss, { dev: true });
console.log(newObjAss);

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var objSpread = { message: 'Hello you!' };
var newObjSpread = _extends({}, objSpread, { dev: true });
console.log(newObjSpread);

Это мое понимание до сих пор. Object.assign() на самом деле стандартизирован, тогда как при распространении объекта ... еще нет. Единственная проблема - поддержка браузера для первого и в будущем, второго тоже.

Играть с кодом здесь

Надеюсь это поможет.

Ответ 6

Я хотел бы обобщить состояние функции ES "слияние распространенных объектов" в браузерах и в экосистеме с помощью инструментов.

спекуляция

Браузеры: в Chrome, в SF, Firefox скоро (версия 60, IIUC)

  • Поддержка браузером "распространяемых свойств", поставляемых в Chrome 60, включая этот сценарий.
  • Поддержка этого сценария НЕ работает в текущем Firefox (59), но работает в моем Firefox Developer Edition. Поэтому я верю, что он будет выпущен в Firefox 60.
  • Safari: не тестировался, но Kangax говорит, что он работает в Desktop Safari 11.1, но не SF 11
  • iOS Safari: не teseted, но Kangax говорит, что он работает в iOS 11.3, но не в iOS 11
  • еще не в краю

Инструменты: Узел 8.7, TS 2.1

связи

Пример кода (удваивается как тест на совместимость)

var x = { a: 1, b: 2 };
var y = { c: 3, d: 4, a: 5 };
var z = {...x, ...y};
console.log(z); // { a: 5, b: 2, c: 3, d: 4 }

Опять же: на момент написания этого примера этот пример работал без перехода в Chrome (60+), Firefox Developer Edition (предварительный просмотр Firefox 60) и Node (8. 7+).

Зачем отвечать?

Я пишу это через 2,5 года после первоначального вопроса. Но у меня был тот же самый вопрос, и это то, куда Google послал меня. Я раб миссии SO, чтобы улучшить длинный хвост.

Поскольку это расширение синтаксиса "разброса по массивам", мне было очень трудно его найти в Google, и его трудно найти в таблицах совместимости. Самое близкое, что я могу найти, - это Kangax "распространение свойств", но в этом тесте нет двух спредов в одном выражении (не слияние). Кроме того, имя на страницах предложений/проектов/браузера использует слово "распространение свойств", но мне кажется, что это был "первый принцип", к которому пришло сообщество после предложений использовать синтаксис распространения для "слияния объектов". (Это может объяснить, почему в Google так сложно.) Поэтому я документирую свои находки здесь, чтобы другие могли просматривать, обновлять и компилировать ссылки об этой конкретной функции. Я надеюсь, что это завоевывает популярность. Пожалуйста, помогите распространять новости о его посадке в спецификации и в браузерах.

Наконец, я бы добавил эту информацию в качестве комментария, но я не мог редактировать их, не нарушив первоначальные намерения авторов. В частности, я не могу редактировать комментарий @ChillyPenguin без потери намерения исправить @RichardSchulte. Но спустя годы Ричард оказался прав (на мой взгляд). Поэтому я пишу этот ответ вместо этого, надеясь, что в конечном итоге он получит поддержку старых ответов (это может занять годы, но в конце концов это то, что представляет собой эффект длинного хвоста).

Ответ 7

ПРИМЕЧАНИЕ: распространение не просто синтаксический сахар вокруг Object.assign. Они работают по-разному за кадром.

Object.assign применяет сеттеры к новому объекту, а Spread - нет. Кроме того, объект должен быть итеративным.

Копировать Используйте это, если вам нужно, чтобы значение объекта было таким, как оно есть в данный момент, и вы не хотите, чтобы это значение отражало какие-либо изменения, сделанные другими владельцами объекта.

Используйте его для создания мелкой копии объекта. Рекомендуется всегда задавать неизменяемые свойства для копирования - поскольку изменяемые версии можно передавать в неизменяемые свойства, функция копирования гарантирует, что вы всегда будете иметь дело с неизменным объектом.

Назначить Назначить несколько противоположно копировать. Assign сгенерирует установщик, который присваивает значение переменной экземпляра напрямую, а не копирует или сохраняет его. При вызове метода получения назначенного свойства он возвращает ссылку на фактические данные.

Ответ 8

Другие ответы старые, не может получить хороший ответ.
Ниже приведен пример для литералов объектов, который помогает понять, как они могут дополнять друг друга, и как они не могут дополнять друг друга (следовательно, разница):

var obj1 = { a: 1,  b: { b1: 1, b2: 'b2value', b3: 'b3value' } };

// overwrite parts of b key
var obj2 = {
      b: {
        ...obj1.b,
        b1: 2
      }
};
var res2 = Object.assign({}, obj1, obj2); // b2,b3 keys still exist
document.write('res2: ', JSON.stringify (res2), '<br>');
// Output:
// res2: {"a":1,"b":{"b1":2,"b2":"b2value","b3":"b3value"}}  // NOTE: b2,b3 still exists

// overwrite whole of b key
var obj3 = {
      b: {
        b1: 2
      }
};
var res3 = Object.assign({}, obj1, obj3); // b2,b3 keys are lost
document.write('res3: ', JSON.stringify (res3), '<br>');
// Output:
  // res3: {"a":1,"b":{"b1":2}}  // NOTE: b2,b3 values are lost

Еще несколько небольших примеров, также для массива и объекта:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

Ответ 9

Теперь это часть ES6, поэтому стандартизирована и также документирована на MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator

Это очень удобно использовать и делает большой смысл наряду с деструкцией объекта.

Одно из оставшихся преимуществ, перечисленных выше, - это динамические возможности Object.assign(), однако это так же просто, как распространение массива внутри литерального объекта. В скомпилированном выводе Babel он использует точно то, что демонстрируется с Object.assign()

Таким образом, правильным ответом было бы использовать распространение объектов, поскольку он теперь стандартизован, широко используется (см. реакцию, сокращение и т.д.), прост в использовании и имеет все функции Object.assign()