对象传播与 Object.assign
这不一定是详尽无遗的。
传播语法
options = {...optionsDefault, ...options};
好处:
如果编写在没有本机支持的环境中执行的代码,您可以只编译此语法(而不是使用 polyfill)。(例如,使用 Babel。)
少啰嗦。
缺点:
最初写这个答案时,这是一个提议,而不是标准化。在使用提案时,请考虑如果您现在用它编写代码并且在朝着标准化方向发展时它不会变得标准化或变化,您会怎么做。这已在 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);
缺点:
- 比较啰嗦。
- 如果编写在没有本机支持的环境中执行的代码,则需要 polyfill。
这是让我感到疑惑的提交。
这与您要问的内容没有直接关系。那个代码没有使用Object.assign()
,而是使用用户代码 ( object-assign
) 做同样的事情。他们似乎在用 Babel 编译代码(并用 Webpack 捆绑它),这就是我所说的:你可以编译的语法。他们显然更喜欢必须将其object-assign
作为依赖项包含在他们的构建中。
对于参考对象 rest/spread 在 ECMAScript 2018 中作为第 4 阶段最终确定。该提案可以在这里找到。
大多数情况下,对象重置和扩展的工作方式相同,关键区别在于扩展定义了属性,而 Object.assign() 设置了它们。这意味着 Object.assign() 会触发 setter。
值得记住的是,除此之外,对象 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}
我认为扩展运算符与Object.assign
当前答案中似乎没有提到的一大区别是扩展运算符不会将源对象的原型复制到目标对象。如果要向对象添加属性并且不想更改它的实例,则必须使用Object.assign
.
编辑:我实际上已经意识到我的例子具有误导性。扩展运算符Object.assign
将第一个参数设置为空对象。在我下面的代码示例中,我将错误作为Object.assign
调用的第一个参数,因此两者并不等效。的第一个参数Object.assign
实际上被修改然后返回,这就是它保留其原型的原因。我在下面添加了另一个示例:
const error = new Error();
error instanceof Error // true
const errorExtendedUsingSpread = {
...error,
...{
someValue: true
}
};
errorExtendedUsingSpread instanceof Error; // false
// What the spread operator desugars into
const errorExtendedUsingImmutableObjectAssign = Object.assign({}, error, {
someValue: true
});
errorExtendedUsingImmutableObjectAssign instanceof Error; // false
// The error object is modified and returned here so it keeps its prototypes
const errorExtendedUsingAssign = Object.assign(error, {
someValue: true
});
errorExtendedUsingAssign instanceof Error; // true
另见:https : //github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md
我想总结一下“扩展对象合并”ES 功能在浏览器中以及通过工具在生态系统中的状态。
规格
- https://github.com/tc39/proposal-object-rest-spread
- 此语言功能是第 4 阶段提案,这意味着它已合并到 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:未测试,但 Kangax 表示它适用于 iOS 11.3,但不适用于 iOS 11
- 还没有在 Edge 中
工具:节点 8.7、TS 2.1
- 的NodeJS以来一直支持8.7(通过Kangax)。当我在本地测试时在 9.8 上确认。
- TypeScript从2.1开始支持它,当前是 2.8
链接
代码示例(兼作兼容性测试)
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年后写这篇文章的。但我有同样的问题,这是谷歌发给我的。我是 SO 改进长尾任务的奴隶。
由于这是“数组传播”语法的扩展,我发现很难用谷歌搜索,也很难在兼容性表中找到。我能找到的最接近的是Kangax "property spread",但该测试在同一表达式中没有两个价差(不是合并)。此外,提案/草案/浏览器状态页面中的名称都使用“属性传播”,但在我看来,这是社区在提议使用“对象合并”传播语法后达成的“第一委托人”。(这可以解释为什么谷歌如此困难。)所以我在这里记录我的发现,以便其他人可以查看、更新和编译有关此特定功能的链接。我希望它能流行起来。请帮助传播它登陆规范和浏览器的消息。
最后,我会添加此信息作为评论,但我无法在不破坏作者原始意图的情况下对其进行编辑。具体来说,我无法编辑@ChillyPenguin 的评论,而不会失去纠正@RichardSchulte 的意图。但几年后,事实证明理查德是对的(在我看来)。所以我写了这个答案,希望它最终能在旧答案上获得吸引力(可能需要数年时间,但这毕竟是长尾效应的全部内容)。
注意:Spread 不仅仅是围绕 Object.assign 的语法糖。他们在幕后的运作方式大不相同。
Object.assign 将 setter 应用于新对象,Spread 不会。此外,对象必须是可迭代的。
复制 如果您需要对象的当前值,并且您不希望该值反映对象的其他所有者所做的任何更改,请使用此选项。
使用它来创建对象的浅表副本 始终将不可变属性设置为复制的良好实践 - 因为可变版本可以传递到不可变属性中,复制将确保您将始终处理不可变对象
分配 分配有点与复制相反。Assign 将生成一个 setter,它将值直接分配给实例变量,而不是复制或保留它。当调用赋值属性的 getter 时,它返回对实际数据的引用。