在 2018 年 Object Rest/Spread Proposal 達到了 stage 4,這意味著在未來它會將入到 ECMAScript 標準中。它也被加入到Node LTS. Node.js 8 以後的版本你可以使用它,所以你可以放心地開始使用它。
Object Spread 也可以叫做物件展開符,下文都以 Object Spread 來進行描述。
$ node -v
v8.9.4
$ node
> const obj = { foo: 1, bar: 1 };
undefined
> ({ ...obj, baz: 1 });
{ foo: 1, bar: 1, baz: 1 }
複製程式碼
Object Spread 和 Object.assign 在功能上很相似。你應該使用哪一個? 事實證明,答案比你想象的要微妙許多。
Object Spread 概論
Object Spread 運算子的基本思想是使用現有物件的自身屬性來建立新的普通物件。 所以{...obj}
建立一個和 obj
具有相同屬性的物件。 對於普通的舊 JavaScript 物件,你實際上是在建立一個obj
副本。
const obj = { foo: `bar` };
const clone = { ...obj }; // `{ foo: `bar` }`
obj.foo = `baz`;
clone.foo; // `bar`
複製程式碼
與object .assign()類似,Object spread 操作符不復制繼承的屬性或類的屬性。但是它會複製 ES6 的 symbols 屬性。
class BaseClass {
foo() { return 1; }
}
class MyClass extends BaseClass {
bar() { return 2; }
}
const obj = new MyClass();
obj.baz = function() { return 3; };
obj[Symbol.for(`test`)] = 4;
// Does _not_ copy any properties from `MyClass` or `BaseClass`
const clone = { ...obj };
console.log(clone); // { baz: [Function], [Symbol(test)]: 4 }
console.log(clone.constructor.name); // Object
console.log(clone instanceof MyClass); // false
複製程式碼
還可以使用 Object spread 操作符混合其他屬性。
順序問題: Object spread 操作符將覆蓋在它之前定義的屬性。
const obj = { a: `a`, b: `b`, c: `c` };
{ a: 1, b: null, c: void 0, ...obj }; // { a: `a`, b: `b`, c: `c` }
{ a: 1, b: null, ...obj, c: void 0 }; // { a: `a`, b: `b`, c: undefined }
{ a: 1, ...obj, b: null, c: void 0 }; // { a: `a`, b: null, c: undefined }
{ ...obj, a: 1, b: null, c: void 0 }; // { a: 1, b: null, c: undefined }
複製程式碼
和 Object.assign() 的區別
對於上面的例子,Object.assign()
函式基本上可以與 Object spread 操作符互換。事實上,object spread spec 明確指出{... obj}
等同於Object.assign({},obj)
。
const obj = { a: `a`, b: `b`, c: `c` };
Object.assign({ a: 1, b: null, c: void 0 }, obj); // { a: `a`, b: `b`, c: `c` }
Object.assign({ a: 1, b: null }, obj, { c: void 0 }); // { a: `a`, b: `b`, c: undefined }
Object.assign({ a: 1 }, obj, { b: null, c: void 0 }); // { a: `a`, b: null, c: undefined }
Object.assign({}, obj, { a: 1, b: null, c: void 0 }); // { a: 1, b: null, c: undefined }
複製程式碼
那麼你為什麼要使用其中一個呢?一個關鍵的區別是 Object spread 操作符總是給你一個POJO(Plain Ordinary JavaScript Object)。而Object.assign()
函式卻修改其第一個傳入物件obj
:
class MyClass {
set val(v) {
console.log(`Setter called`, v);
return v;
}
}
const obj = new MyClass();
Object.assign(obj, { val: 42 }); // Prints "Setter called 42"
複製程式碼
換句話說,Object.assign()
修改了一個物件,因此它可以觸發 ES6 setter。如果你更喜歡使用immutable技術,那麼 Object spread 操作符就是你更好的選擇。使用 Object.assign()
,你必須確保始終將空物件{}
作為第一個引數傳遞。
2019.02.12 補充說明,當一個 Object 使用了 Object.defineProperty 修改了 set 方法,因為呼叫 Object.assign 會觸發 setter 方法,會觸發意想不到的錯誤。
效能怎麼樣? 這是一些簡單的基準測試。如果將空物件作為第一個引數傳遞給Object.assign()
,看起來 Object spread 會更快,但除此之外它們是可互換的。
下面是一個使用Object.assign()
和in-place賦值的基準測試:
const Benchmark = require(`benchmark`);
const suite = new Benchmark.Suite;
const obj = { foo: 1, bar: 2 };
suite.
add(`Object spread`, function() {
({ baz: 3, ...obj });
}).
add(`Object.assign()`, function() {
Object.assign({ baz: 3 }, obj);
}).
on(`cycle`, function(event) {
console.log(String(event.target));
}).
on(`complete`, function() {
console.log(`Fastest is ` + this.filter(`fastest`).map(`name`));
}).
run({ `async`: true });
複製程式碼
在這種情況下,兩者是相似的:
Object spread x 3,170,111 ops/sec +-1.50% (90 runs sampled)
Object.assign() x 3,290,165 ops/sec +-1.86% (88 runs sampled)
Fastest is Object.assign()
複製程式碼
但是,一旦向Object.assign()
輸入一個空物件引數,物件擴充套件運算子就會更快
suite.
add(`Object spread`, function() {
({ baz: 3, ...obj });
}).
add(`Object.assign()`, function() {
Object.assign({}, obj, { baz: 3 });
})
複製程式碼
這是輸出:
Object spread x 3,065,831 ops/sec +-2.12% (85 runs sampled)
Object.assign() x 2,461,926 ops/sec +-1.52% (88 runs sampled)
Fastest is Object spread
複製程式碼
ESLint 配置
預設情況下,ESLint在解析層面禁止物件rest / spread運算子你需要在.eslintrc.yml中將parserOptions.ecmaVersion選項設定為至少9,否則你將得到一個解析錯誤。
parserOptions:
# Otherwise object spread causes `Parsing error: Unexpected token ..`
ecmaVersion: 9
複製程式碼
ESLint新增了一個新的規則prefer-object-spread,它會強制你使用 Object spread 操作符 而不是Object.assign()
。 要啟用此規則,請使用:
parserOptions:
ecmaVersion: 9
rules:
prefer-object-spread: error
複製程式碼
現在,如果您使用object .assign()
而不是Object spread, ESLint將報告一個錯誤。
Use an object spread instead of `Object.assign` eg: `{ ...foo }` prefer-object-spread
複製程式碼
最後
Object rest / spread運算子在語法更加簡潔,並且比Object.assign()
提供了效能優勢。 如果你執行的是Node.js 8或更高版本,請嘗試使用這些新運算子,使程式碼更簡潔。
更多請關注
友情連結: huayifeng.top/