點燈坊

失くすものさえない今が強くなるチャンスよ

如何取得 Object 所有 Key ?

Sam Xiao's Avatar 2021-03-01

ECMAScript Object 的 Property 基本上是由 Key / Value 構成,連 Method 也是廣義 Property,我們該如何取得 Object 的所有 Key 呢 ?

Version

ECMAScript 2015

Object.keys()

Object Literal

let person = {
  firstName: 'Sam',
  lastName: 'Xiao',
  fullName: function() {
    return this.firstName + ' ' + this.lastName
  }
}

Object.keys(person) // ?

以 object literal 定義 person,在 object literal 的 property 都是 enumerable,所以 Object.keys() 都能顯示。

Object.keys()
顯示 Object 所有 enumerable property 的 key,但不包含 prototype 的 property

key000

Object.create()

let person = Object.create({}, {
  firstName: {
    value: 'Sam',
    writable: true,
    configurable: true,
    enumerable: true,
  },
  lastName: {
    value: 'Xiao',
    writable: true,
    configurable: true,
    enumerable: false,
  },
  fullName: {
    value: function() {
      return `${this.firstName} ${this.lastName}`
    },
    writable: true,
    configurable: true,
    enumerable: true,
  },
})

Object.keys(person) // ?

若要特別建立 non enumerable property,就必須使用 Object.create(),並特別設定是否為 enumerable。

第 8 行

lastName: {
  value: 'Xiao',
  writable: true,
  configurable: true,
  enumerable: false,
},

特別設定 lastName property 的 enumerablefalse,則 Object.keys() 將無法顯示 lastName

key002

Prototype

function Person(firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

Person.prototype.fullName = function() {
  return `${this.firstName} ${this.lastName}`
}

let person = new Person('Sam', 'Xiao')

Object.keys(person) // ?

ECMAScript 的 OOP 正統寫法,是將 method 定義在 prototype,也因為定義在 prototype,Object.keys() 無法抓到該 method。

key001

Class

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }

  fullName() {
    return `${this.firstName} ${this.lastName}`
  }
}

let person = new Person('Sam', 'Xiao')

Object.keys(person) // ?

若使用 ECMAScript 2015 的 class 寫法,Object.keys() 也只能抓到 firstNamelastName,別忘了 class 本質仍是 constructor function,其 method 也是定義在 prototype,因此 Object.keys() 抓不到很合理。

key006

Object.getOwnPropertyNames()

Object Literal

let person = {
  firstName: 'Sam',
  lastName: 'Xiao',
  fullName: function() {
    return this.firstName + ' ' + this.lastName
  }
}

Object.getOwnPropertyNames(person) // ?

使用 Object.getOwnPropertyNames() 顯示 person 所有 property 的 key。

object.getOwnPropertyNames()
顯示 Object 所有 property 的 key,無論是 enumerable 或 non enumerable,但不包含 prototype 的 property

key008

Object.create()

let person = Object.create({}, {
  firstName: {
    value: 'Sam',
    writable: true,
    configurable: true,
    enumerable: true,
  },
  lastName: {
    value: 'Xiao',
    writable: true,
    configurable: true,
    enumerable: false,
  },
  fullName: {
    value: function() {
      return `${this.firstName} ${this.lastName}`
    },
    writable: true,
    configurable: true,
    enumerable: true,
  },
})

Object.getOwnPropertyNames(person) // ?

僅管 lastNameEnumerablefalse,但 Object.getOwnPropertyNames() 仍然會顯示。

key003

Prototype

let person = Object.create({
  fullName: function() {
    return `${this.firstName} ${this.lastName}`
  }}, {
  firstName: {
    value: 'Sam',
    writable: true,
    configurable: true,
    enumerable: true,
  },
  lastName: {
    value: 'Xiao',
    writable: true,
    configurable: true,
    enumerable: false,
  },
})

Object.getOwnPropertyNames(person) // ?

fullName() 移到 prototype,而 object literal 都是 enumerable property。

但因為 Object.getOwnPropertyNames() 不會顯示 prototype 的 property,因此只能顯示 firstNamelastName

key004

Class

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }

  fullName() {
    return `${this.firstName} ${this.lastName}`
  }
}

let person = new Person('Sam', 'Xiao')

Object.getOwnPropertyNames(person) // ?

若使用 ECMAScript 2015 的 class 寫法,Object.getOwnPropertyNames() 也只能抓到 firstNamelastName,別忘了 class 本質仍是 constructor function,其 method 也是定義在 prototype,因此 Object.getOwnPropertyNames() 抓不到很合理。

key007

for…in

Object Literal

let person = {
  firstName: 'Sam',
  lastName: 'Xiao',
  fullName: function() {
    return this.firstName + ' ' + this.lastName
  }
}

for (let key in person)
  console.log(key)

for...in 能顯示所有 enumerable,因此能顯示 object literal 所有 property 的 key。

for…in
如同 Object.keys() 將 Object 所有 enumerable property 的 key 以 array 傳回,但包含 prototype 的 property

key009

Object.create()

let person = Object.create({}, {
  firstName: {
    value: 'Sam',
    writable: true,
    configurable: true,
    enumerable: true,
  },
  lastName: {
    value: 'Xiao',
    writable: true,
    configurable: true,
    enumerable: false,
  },
  fullName: {
    value: function() {
      return `${this.firstName} ${this.lastName}`
    },
    writable: true,
    configurable: true,
    enumerable: true,
  },
})

for (let key in person)
  console.log(key)

for...in 無法顯示 non enumerable property。

key010

Prototype

function Person(firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

Person.prototype.fullName = function() {
  return `${this.firstName} ${this.lastName}`
};

let person = new Person('Sam', 'Xiao')

for (let key in person)
  console.log(key)

我們發現無論 Object.keys()Object.getOwnPropertyNames(),都無法顯示 prototype 的 property,但 for...in 可顯示。

key005

Class

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }

  fullName() {
    return `${this.firstName} ${this.lastName}`
  }
}

let person = new Person('Sam', 'Xiao')

for (let key in person)
  console.log(key)

for...in 搭配 ECMAScript 2015 的 class 時,卻不會顯示 fullName

理論上 classfullName() 是在 prototype 上,for...in 應該會顯示卻沒顯示,也就是若搭配 classfor...in 相當於 Object.keys(),結果比較令人訝異。

key011

Summary

Object.keys() Object.getOwnPropertyNames() for in
Enumerable Yes Yes Yes
Non Enumerable No Yes No
Prototype No No Yes
Class No Method No Method No Method
  • Object.keys() 最中規中矩,不能顯示 non enumerable,也不能顯示 prototype
  • Object.getOwnPropertyNames() 有特異功能可顯示 non enumerable,但無法顯示 prototype
  • for in 有特異功能能顯示 Prototype,但無法顯示 non enumerable
  • 三種寫法都無法顯示 class 的 method

Conclusion

  • 三種方式都各有其限制,要視需求選擇合適的方式
  • Object.keys()for..in 搭配 class 時,竟然結果一樣,比較出乎意外

Reference

MDN, Object.keys()
MDN, Object.getOwnPropertyNames()
MDN, for in