Распространение объекта против 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()