假设我有一个选项变量我想设置一个默认值。
这两种选择的优点/缺点是什么?
使用对象扩展
options = {...optionsDefault, ...options};
或者使用Object.assign
options = Object.assign({}, optionsDefault, options);
这是让我疑惑的承诺。
假设我有一个选项变量我想设置一个默认值。
这两种选择的优点/缺点是什么?
使用对象扩展
options = {...optionsDefault, ...options};
或者使用Object.assign
options = Object.assign({}, optionsDefault, options);
这是让我疑惑的承诺。
当前回答
对象扩展操作符(…)在浏览器中不起作用,因为它还不是任何ES规范的一部分,只是一个建议。唯一的选择是用Babel(或类似的东西)编译它。
正如您所看到的,它只是Object.assign({})上的语法糖。
在我看来,这些是重要的区别。
对象。赋值在大多数浏览器中都有效(不需要编译) ... For对象不是标准化的 ... 防止您意外地改变对象 ... 将填充对象。在没有它的浏览器中赋值 ... 需要更少的代码来表达相同的思想
其他回答
这并不一定详尽无遗。
传播的语法
options = {...optionsDefault, ...options};
优点:
如果在没有本机支持的环境中编写代码以执行,则可以只编译此语法(而不是使用polyfill)。(比如巴别塔。) 可以减少复杂性。
缺点:
当这个答案最初被写出来的时候,这是一个建议,没有标准化。在使用提案时,请考虑如果您现在使用提案编写代码,而它没有得到标准化或在向标准化发展的过程中发生变化,该怎么办。这已经在ES2018中标准化了。 字面的,不是动态的。
Object.assign ()
options = Object.assign({}, optionsDefault, options);
优点:
标准化。 动态的。例子: var sources = [{a: " a "}, {b: " b "}, {c: " c "}]; options = Object.assign。应用(对象,({}).concat(来源)); / /或 options =对象。分配({},…来源);
缺点:
更详细的。 如果在没有本机支持的环境中编写代码执行,则需要填充。
这是让我疑惑的承诺。
这和你问的问题没有直接关系。这段代码没有使用Object.assign(),而是使用用户代码(object-assign)来做同样的事情。他们似乎在用Babel编译代码(并将其与Webpack捆绑),这就是我所说的:你可以编译的语法。他们显然更喜欢这样,而不是必须将对象赋值作为一个依赖项包含到他们的构建中。
这现在是ES6的一部分,因此是标准化的,并且在MDN上也有文档: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator
它使用起来非常方便,与对象解构一起使用也很有意义。
上面列出的另一个优势是object. assign()的动态功能,不过这就像在文字对象中展开数组一样简单。在编译后的babel输出中,它完全使用了Object.assign()所演示的内容
因此,正确的答案是使用object spread,因为它现在是标准化的,被广泛使用(参见react, redux等),易于使用,并且具有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!
使用JavaScript时可能不清楚。但是如果你想创建一些类的实例,使用TypeScript会更容易
const spread: SomeClass = {...new SomeClass()} // Error
其他答案都老了,得不到好的答案。 下面的例子是对象字面量,有助于两者如何相互补充,以及如何不能相互补充(因此存在差异):
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
两者之间有着巨大的差异,并会带来非常严重的后果。投票最多的问题甚至没有触及到这一点,关于物体传播是一项提案的信息在2022年已经无关紧要了。
区别在于对象。赋值操作将对象就地更改,而展开操作符(…)将创建一个全新的对象,这将破坏对象引用相等性。
首先,让我们看看效果,然后我将给出一个现实世界的例子,说明理解这种根本差异是多么重要。
首先,让我们使用Object.assign:
// Let's create a new object, that contains a child object;
const parentObject = { childObject: { hello: 'world '} };
// Let's get a reference to the child object;
const childObject = parentObject.childObject;
// Let's change the child object using Object.assign, adding a new `foo` key with `bar` value;
Object.assign(parentObject.childObject, { foo: 'bar' });
// childObject is still the same object in memory, it was changed IN PLACE.
parentObject.childObject === childObject
// true
现在对展开运算符进行同样的练习:
// Let's create a new object, that contains a child object;
const parentObject = { childObject: { hello: 'world '} };
// Let's get a reference to the child object;
const childObject = parentObject.childObject;
// Let's change the child object using the spread operator;
parentObject.childObject = {
...parentObject.childObject,
foo: 'bar',
}
// They are not the same object in memory anymore!
parentObject.childObject === childObject;
// false
很容易看出发生了什么,因为在parentObject上。childObject ={…}我们清楚地将parentObject中的childObject键的值赋给一个全新的对象文字,而它是由旧的childObject内容组成的事实是无关紧要的。这是一个新物件。
如果你认为这在实践中是无关紧要的,让我展示一个真实世界的场景来理解它有多重要。
在一个非常大的Vue.js应用程序中,当我们在输入字段中输入客户的名字时,我们开始注意到很多延迟。
经过大量调试后,我们发现输入中的每个字符都会触发一大堆需要重新计算的计算属性。
这是意料之外的,因为在那些计算函数中根本没有使用客户的名称。只使用了其他客户数据(如年龄、性别)。发生了什么事?当客户的名字改变时,vue为什么要重新计算所有这些计算函数?
我们有一家Vuex商店是这样做的:
mutations: {
setCustomer(state, payload) {
// payload being { name: 'Bob' }
state.customer = { ...state.customer, ...payload };
}
我们的计算是这样的:
veryExpensiveComputed() {
const customerAge = this.$store.state.customer.age;
}
所以,瞧!当客户名称更改时,Vuex突变实际上是将其完全更改为一个新对象;由于计算依赖于该对象来获取客户年龄,Vue依赖于该非常特定的对象实例作为依赖项,当它被更改为一个新对象时(未能通过===对象相等性测试),Vue决定是时候重新运行计算函数了。
这是固定的吗?使用对象。赋值不丢弃前一个对象,而是在适当的位置更改它…
mutations: {
setCustomer(state, payload) {
// payload being same as above: { name: 'Bob' }
Object.assign(state.customer, payload);
}
顺便说一句,如果你在ve2中,你不应该使用Object。因为Vue 2不能直接跟踪这些对象的变化,但同样的逻辑适用,只是使用Vue。set而不是Object.assign:
mutations: {
setCustomer(state, payload) {
Object.keys(payload).forEach(key => {
Vue.set(state.customer, key, payload[key])
})
}