Object.keys()
與 for...in
的差異在於 Object.keys()
只能顯示目前 Object 的 Property,而 for...in
會連同 Prototype 的 Property 一併顯示。若 for...in
搭配 Constructor Function 或 Object.create()
時一切正常,但搭配 class 時,就無法顯示 Prototype 的 Property 了,為什麼會這樣呢 ?
Version
macOS Catalina 10.15.2
VS Code 1.41.1
Quokka 1.0.267
ECMAScript 2015
Constructor Function
function Book(title, price) {
this.title = title
this.price = price
}
Book.prototype.showDescription = function() {
return `${this.title} : ${this.price}`
}
let book = new Book('FP in JavaScript', 100)
for(let key in book)
console.log(key)
由 constructor function 建立 object,並將 method 定義在其 prototype,這是 ECMAScript OOP 標準寫法。
當對 object 使用 for...in
loop,會顯示所有的 property,連 prototype 也會顯示。
Object.create()
let prototype = {
showDescription: function() {
return `${this.title} : ${this.price}`
}
}
let book = Object.create(prototype)
book.title = 'FP in JavaScript'
book.price = 100
for(let key in book)
console.log(key)
Prototype 除了事後在 constructor function 動態指定外,也可以事先建立 prototype object,然後傳入 Object.create()
。
一樣使用 for...in
loop,也如預期列出 prototype 的 property。
Class
class Book {
constructor(title, price) {
this.title = title
this.price = price
}
showDescription() {
return `${this.title} / ${this.price}`
}
}
let book = new Book('FP in JavaScript', 100)
for(let key in book)
console.log(key)
ES6 導入 class
後,method 可以直接定義在 class 內,會自己幫我們定義在 prototype。
但使用 for...in
loop,卻發現 prototype 的 showDescription()
無法顯示,退化成與 Object.keys()
功能一樣,為什麼會這樣呢 ?
Why ?
for...in
與 Object.keys()
都僅能列出 enumerable property,所以很有可能 class 寫法造成 prototype 的 property 為 nonenumerable,使得 for...in
無法顯示。
class Book {
constructor(title, price) {
this.title = title
this.price = price
}
showDescription() {
return `${this.title} / ${this.price}`
}
}
let book = new Book('FP in JavaScript', 100)
Object.getOwnPropertyDescriptor(book.__proto__, 'showDescription') // ?
Object.getOwnPropertyDescriptor()
會傳回每個 property 的屬性,藉此觀察是否為 nonenumerable。
也因為 enumerable
為 false
,所以 for...in
loop 無法顯示。
Conclusion
for...in
為 ECMAScript 3 所定義,而Object.keys()
為 ECMAScript 5.1 所加入,理論上兩者的差異就在於 prototype 部分,但 ES6 支援 class 後,又使得for..in
與Object.keys()
功能趨於一至,個人是不太喜歡這種改變,這使得for...in
與過去的觀念不同,算 breaking change,但既然 ES6 規格就是這樣定義,也只能自己注意這個微小的差異了