Javascript 对象传播与 Object.assign

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32925460/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 14:23:58  来源:igfitidea点击:

Object spread vs. Object.assign

javascriptecmascript-6

提问by Olivier Tassinari

Let's say I have an optionsvariable and I want to set some default value.

假设我有一个options变量,我想设置一些默认值。

What's is the benefit / drawback of these two alternatives?

这两种替代方案的优点/缺点是什么?

Using object spread

使用对象展开

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

Or using Object.assign

或者使用 Object.assign

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

This is the committhat made me wonder.

这是让我感到疑惑的提交

采纳答案by JMM

This isn't necessarily exhaustive.

这不一定是详尽无遗的。

Spread syntax

传播语法

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

Advantages:

好处:

  • If authoring code for execution in environments without native support, you may be able to just compile this syntax (as opposed to using a polyfill). (With Babel, for example.)

  • Less verbose.

  • 如果编写在没有本机支持的环境中执行的代码,您可以只编译此语法(而不是使用 polyfill)。(例如,使用 Babel。)

  • 少啰嗦。

Disadvantages:

缺点:

  • When this answer was originally written, this was a proposal, not standardized. When using proposals consider what you'd do if you write code with it now and it doesn't get standardized or changes as it moves toward standardization. This has since been standardized in ES2018.

  • Literal, not dynamic.

  • 最初写这个答案时,这是一个提议,而不是标准化。在使用提案时,请考虑如果您现在用它编写代码并且在朝着标准化方向发展时它不会变得标准化或变化,您会怎么做。这已在 ES2018 中标准化。

  • 字面意思,不是动态的。



Object.assign()

Object.assign()

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

Advantages:

好处:

  • Standardized.

  • Dynamic. Example:

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

  • 动态的。例子:

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

Disadvantages:

缺点:

  • More verbose.
  • If authoring code for execution in environments without native support you need to polyfill.
  • 比较啰嗦。
  • 如果编写在没有本机支持的环境中执行的代码,则需要 polyfill。


This is the commit that made me wonder.

这是让我感到疑惑的提交。

That's not directly related to what you're asking. That code wasn't using Object.assign(), it was using user code (object-assign) that does the same thing. They appear to be compiling that code with Babel (and bundling it with Webpack), which is what I was talking about: the syntax you can just compile. They apparently preferred that to having to include object-assignas a dependency that would go into their build.

这与您要问的内容没有直接关系。那个代码没有使用Object.assign(),而是使用用户代码 ( object-assign) 做同样的事情。他们似乎在用 Babel 编译代码(并用 Webpack 捆绑它),这就是我所说的:你可以编译的语法。他们显然更喜欢必须将其object-assign作为依赖项包含在他们的构建中。

回答by tomhughes

For reference object rest/spread is finalised in ECMAScript 2018 as a stage 4. The proposal can be found here.

对于参考对象 rest/spread 在 ECMAScript 2018 中最终确定为第 4 阶段。该提案可以在这里找到。

For the most part object reset and spread work the same way, the key difference is that spread defines properties, whilst Object.assign() sets them. This means Object.assign() triggers setters.

大多数情况下,对象重置和扩展的工作方式相同,关键区别在于扩展定义了属性,而 Object.assign() 设置了它们。这意味着 Object.assign() 会触发 setter。

It's worth remembering that other than this, object rest/spread 1:1 maps to Object.assign() and acts differently to array (iterable) spread. For example, when spreading an array null values are spread. However using object spread null values are silently spread to nothing.

值得记住的是,除此之外,对象 rest/spread 1:1 映射到 Object.assign() 并且行为与数组(可迭代)传播不同。例如,当传播数组时,空值被传播。然而,使用对象传播空值被悄悄地传播为空。

Array (Iterable) Spread Example

数组(可迭代)扩展示例

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

Object Spread Example

对象传播示例

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

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

This is consistent with how Object.assign() would work, both silently exclude the null value with no error.

这与 Object.assign() 的工作方式一致,两者都默默地排除空值而没有错误。

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

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

回答by Sean Dawson

I think one big difference between the spread operator and Object.assignthat doesn't seem to be mentioned in the current answers is that the spread operator will not copy the the source object's prototype to the target object. If you want to add properties to an object and you don't want to change what instance it is of, then you will have to use Object.assign. The example below should demonstrate this:

我认为扩展运算符与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

回答by Michael Giovanni Pumo

As others have mentioned, at this moment of writing, Object.assign()requires a polyfill and object spread ...requires some transpiling (and perhaps a polyfill too) in order to work.

正如其他人所提到的,在撰写本文时,Object.assign()需要一个...polyfill并且对象传播需要一些转换(也可能是一个 polyfill)才能工作。

Consider this code:

考虑这个代码:

// 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);

These both produce the same output.

这些都产生相同的输出。

Here is the output from Babel, to ES5:

这是从 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);

This is my understanding so far. Object.assign()is actually standardised, where as object spread ...is not yet. The only problem is browser support for the former and in future, the latter too.

这是我目前的理解。Object.assign()实际上是标准化的,而对象传播...尚未标准化。唯一的问题是浏览器支持前者,将来也会支持后者。

Play with the code here

在这里玩代码

Hope this helps.

希望这可以帮助。

回答by yzorg

I'd like to summarize status of the "spread object merge" ES feature, in browsers, and in the ecosystem via tools.

我想总结一下“扩展对象合并”ES 功能在浏览器中以及通过工具在生态系统中的状态。

Spec

规格

Browsers: in Chrome, in SF, Firefox soon (ver 60, IIUC)

浏览器:Chrome、SF、Firefox 即将推出(版本 60,IIUC)

  • Browser support for "spread properties" shipped in Chrome 60, including this scenario.
  • Support for this scenario does NOT work in current Firefox (59), but DOES work in my Firefox Developer Edition. So I believe it will ship in Firefox 60.
  • Safari: not tested, but Kangax says it works in Desktop Safari 11.1, but not SF 11
  • iOS Safari: not teseted, but Kangax says it works in iOS 11.3, but not in iOS 11
  • not in Edge yet
  • 浏览器支持Chrome 60 中提供的“传播属性” ,包括这种情况。
  • 对这种情况的支持在当前的 Firefox (59) 中不起作用,但在我的 Firefox Developer Edition 中起作用。所以我相信它会在 Firefox 60 中发布。
  • Safari:未测试,但 Kangax 表示它适用于 Desktop Safari 11.1,但不适用于 SF 11
  • iOS Safari:未测试,但 Kangax 表示它适用于 iOS 11.3,但不适用于 iOS 11
  • 还没有在 Edge 中

Tools: Node 8.7, TS 2.1

工具:节点 8.7、TS 2.1

Links

链接

Code Sample (doubles as compatibility test)

代码示例(兼作兼容性测试)

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 }

Again: At time of writing this sample works without transpilation in Chrome (60+), Firefox Developer Edition (preview of Firefox 60), and Node (8.7+).

再说一遍:在编写此示例时,该示例无需在 Chrome (60+)、Firefox Developer Edition(Firefox 60 预览版)和 Node (8.7+) 中进行转译即可工作。

Why Answer?

为什么要回答?

I'm writing this 2.5 yearsafter the original question. But I had the very same question, and this is where Google sent me. I am a slave to SO's mission to improve the long tail.

我是在原始问题提出2.5后写这篇文章的。但我有同样的问题,这是谷歌发给我的。我是 SO 改善长尾任务的奴隶。

Since this is an expansion of "array spread" syntax I found it very hard to google, and difficult to find in compatibility tables. The closest I could find is Kangax "property spread", but that test doesn't have two spreads in the same expression (not a merge). Also, the name in the proposals/drafts/browser status pages all use "property spread", but it looks to me like that was a "first principal" the community arrived at after the proposals to use spread syntax for "object merge". (Which might explain why it is so hard to google.) So I document my finding here so others can view, update, and compile links about this specific feature. I hope it catches on. Please help spread the news of it landing in the spec and in browsers.

由于这是“数组传播”语法的扩展,我发现很难用谷歌搜索,并且很难在兼容性表中找到。我能找到的最接近的是Kangax "property spread",但该测试在同一表达式中没有两个价差(不是合并)。此外,提案/草稿/浏览器状态页面中的名称都使用“property spread”,但在我看来,这是社区在提案使用“对象合并”的 spread 语法后达成的“第一委托人”。(这可以解释为什么谷歌如此困难。)所以我在这里记录我的发现,以便其他人可以查看、更新和编译有关此特定功能的链接。我希望它能流行起来。请帮助传播它登陆规范和浏览器的消息。

Lastly, I would have added this info as a comment, but I couldn't edit them without breaking the authors' original intent. Specifically, I can't edit @ChillyPenguin's comment without it losing his intent to correct @RichardSchulte. But years later Richard turned out to be right (in my opinion). So I write this answer instead, hoping it will gain traction on the old answers eventually (might take years, but that's what the long taileffect is all about, after all).

最后,我会添加此信息作为评论,但我无法在不破坏作者原始意图的情况下对其进行编辑。具体来说,我无法编辑@ChillyPenguin 的评论,而不会失去纠正@RichardSchulte 的意图。但几年后,事实证明理查德是对的(在我看来)。所以我写了这个答案,希望它最终能在旧答案上获得吸引力(可能需要数年时间,但这毕竟是长尾效应的全部内容)。

回答by Karthick Kumar

The object spread operator (...) doesn't work in browsers, because it isn't part of any ES specification yet, just a proposal. The only option is to compile it with Babel (or something similar).

对象扩展运算符 (...) 在浏览器中不起作用,因为它还不是任何 ES 规范的一部分,只是一个提案。唯一的选择是用 Babel(或类似的东西)编译它。

As you can see, it's just syntactic sugar over Object.assign({}).

如您所见,它只是 Object.assign({}) 的语法糖。

As far as I can see, these are the important differences.

据我所知,这些是重要的区别。

  • Object.assign works in most browsers (without compiling)
  • ...for objects isn't standardized
  • ...protects you from accidentally mutating the object
  • ...will polyfill Object.assign in browsers without it
  • ...needs less code to express the same idea
  • Object.assign 适用于大多数浏览器(无需编译)
  • ...对于对象没有标准化
  • ...保护您不会意外地改变对象
  • ...将在没有它的浏览器中 polyfill Object.assign
  • ...需要更少的代码来表达相同的想法

回答by Charles Owen

NOTE: Spread is NOT just syntactic sugar around Object.assign. They operate much differently behind the scenes.

注意:Spread 不仅仅是围绕 Object.assign 的语法糖。他们在幕后的运作方式大不相同。

Object.assign applies setters to a new object, Spread does not. In addition, the object must be iterable.

Object.assign 将 setter 应用于新对象,Spread 不会。此外,对象必须是可迭代的。

CopyUse this if you need the value of the object as it is at this moment, and you don't want that value to reflect any changes made by other owners of the object.

复制如果您需要对象此时的值,并且您不希望该值反映该对象的其他所有者所做的任何更改,请使用此选项。

Use it for creating a shallow copy of the object good practice to always set immutable properties to copy - because mutable versions can be passed into immutable properties, copy will ensure that you'll always be dealing with an immutable object

使用它来创建对象的浅拷贝 良好的做​​法是始终将不可变属性设置为复制 - 因为可变版本可以传递到不可变属性中,复制将确保您将始终处理不可变对象

AssignAssign is somewhat the opposite to copy. Assign will generate a setter which assigns the value to the instance variable directly, rather than copying or retaining it. When calling the getter of an assign property, it returns a reference to the actual data.

分配分配有点与复制相反。Assign 将生成一个 setter,它将值直接分配给实例变量,而不是复制或保留它。当调用赋值属性的 getter 时,它返回对实际数据的引用。

回答by Manohar Reddy Poreddy

Other answers are old, could not get a good answer.
Below example is for object literals, helps how both can complement each other, and how it cannot complement each other (therefore difference):

其他答案很旧,无法得到好的答案。
下面的例子是对象字面量,有助于两者如何相互补充,以及它如何不能相互补充(因此有所不同):

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

Several more small examples here, also for array & object:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

这里还有几个小例子,也适用于数组和对象:https:
//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

回答by Rikki Schulte

This is now part of ES6, thus is standardized, and is also documented on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator

这现在是 ES6 的一部分,因此是标准化的,并且也记录在 MDN 上:https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator

It's very convenient to use and makes a lot of sense alongside object destructuring.

它使用起来非常方便,并且与对象解构一起使用很有意义。

The one remaining advantage listed above is the dynamic capabilities of Object.assign(), however this is as easy as spreading the array inside of a literal object. In the compiled babel output it uses exactly what is demonstrated with Object.assign()

上面列出的一个剩余优势是 Object.assign() 的动态功能,但这就像在文字对象内部传播数组一样简单。在编译的 babel 输出中,它完全使用了 Object.assign() 所展示的内容

So the correct answer would be to use object spread since it is now standardized, widely used (see react, redux, etc), is easy to use, and has all the features of Object.assign()

所以正确的答案是使用 object spread,因为它现在标准化了,广泛使用(参见 react,redux 等),易于使用,并且具有 Object.assign() 的所有功能

回答by zemil

I'd like to add this simple example when you have to use Object.assign.

当您必须使用 Object.assign 时,我想添加这个简单的示例。

class SomeClass {
  constructor() {
    this.someValue = 'some value';
  }

  someMethod() {
    console.log('some action');
  }
}


const objectAssign = Object.assign(new SomeClass(), {});
objectAssign.someValue; // ok
objectAssign.someMethod(); // ok

const spread = {...new SomeClass()};
spread.someValue; // ok
spread.someMethod(); // there is no methods of SomeClass!

It can be not clear when you use JavaScript. But with TypeScript it is easier if you want to create instance of some class

使用 JavaScript 时可能不清楚。但是如果你想创建某个类的实例,使用 TypeScript 会更容易

const spread: SomeClass = {...new SomeClass()} // Error