1 可枚舉性
1.1 Object.getOwnPropertyDescriptor()
對象的每一個屬性都有一個描述對象(
Descriptor
),用來控制該屬性的行為。使用
Object.getOwnPropertyDescriptor
方法可以擷取該屬性的描述對象。例如:
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
=> {
value: 123,
writable: true,
enumerable: true,
configurable: true
}
描述對象的
enumerable
屬性,稱為“可枚舉性”,如果該屬性為
false
,就表示某些操作會忽略目前屬性。目前,有四個操作會忽略
enumerable
為
false
的屬性。
-
循環:隻周遊對象自身的和繼承的可枚舉的屬性。for...in
-
:傳回對象自身的所有可枚舉的屬性的鍵名。[推薦使用Object.keys()
周遊]Object.keys()
-
:隻串行化對象自身的可枚舉的屬性。JSON.stringify()
-
: 忽略Object.assign()
為enumerable
的屬性,隻拷貝對象自身的可枚舉的屬性。false
這四個操作之中,前三個是
ES5
就有的,最後一個
Object.assign()
是
ES6
新增的。其中,隻有
for...in
會傳回繼承的屬性,其他三個方法都會忽略繼承的屬性,隻處理對象自身的屬性。實際上,引入“可枚舉”(
enumerable
)這個概念的最初目的,就是讓某些屬性可以規避掉
for...in
操作,不然所有内部屬性和方法都會被周遊到。比如,對象原型的
toString
方法,以及數組的
length
屬性,就通過“可枚舉性”,進而避免被
for...in
周遊到。
Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable
// false
Object.getOwnPropertyDescriptor([], 'length').enumerable
// false
上面代碼中,
toString
和
length
屬性的
enumerable
都是
false
,是以
for...in
不會周遊到這兩個繼承自原型的屬性。另外,
ES6
規定,所有
Class
的原型的方法都是不可枚舉的。
Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable
// false
總的來說,操作中引入繼承的屬性會讓問題複雜化,大多數時候,我們隻關心對象自身的屬性。是以,盡量不要用
for...in
循環,而用
Object.keys()
代替。
1.2 Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptors
方法,傳回指定對象所有自身屬性(非繼承屬性)的描述對象。
const obj = { foo: 123, get bar() { return 'abc' } };
Object.getOwnPropertyDescriptors(obj)
=> { foo:
{ value: 123,
writable: true,
enumerable: true,
configurable: true },
bar:
{ get: [Function: bar],
set: undefined,
enumerable: true,
configurable: true }
}
Object.getOwnPropertyDescriptors(obj)
方法傳回一個對象,所有原對象的屬性名都是該對象的屬性名,對應的屬性值就是該屬性的描述對象。
1.3 Object.setPrototypeOf()
用于設定一個對象的原型對象。
// 格式
Object.setPrototypeOf(object, prototype)
// 用法
const o = Object.setPrototypeOf({}, null);
// 該方法等同于下面的函數。
function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
例如:
let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);
proto.y = 20;
proto.z = 40;
obj.x // 10
obj.y // 20
obj.z // 40
【注意】:
- 如果第一個參數不是對象,會自動轉為對象。但是由于傳回的還是第一個參數,是以這個操作不會産生任何效果。
- 由于
和undefined
無法轉為對象,是以如果第一個參數是null
或undefined
,就會報錯。null
1.4 Object.getPrototypeOf()
該方法與
Object.setPrototypeOf
方法配套,用于讀取一個對象的原型對象。
Object.getPrototypeOf(obj);
【注意】:
- 如果參數不是對象,會被自動轉為對象。
- 如果參數是
或undefined
,它們無法轉為對象,是以會報錯。null
1.5 super關鍵字
我們知道,
this
關鍵字總是指向函數所在的目前對象,
ES6
又新增了另一個類似的關鍵字
super
,指向目前對象的原型對象。
const proto = { foo: 'hello' };
const obj = {
find() {
return super.foo;
}
};
Object.setPrototypeOf(obj, proto);
obj.find() // "hello"
// 上面代碼中,對象obj的find方法之中,通過super.foo引用了原型對象proto的foo屬性。
注意,
super
關鍵字表示原型對象時,隻能用在對象的方法之中,用在其他地方都會報錯。
JavaScript
引擎内部,
super.foo
等同于
Object.getPrototypeOf(this).foo
(屬性)或
Object.getPrototypeOf(this).foo.call(this)
(方法)。
例如:
const proto = {
x: 'hello',
foo() {
console.log(this.x);
},
};
const obj = {
x: 'world',
foo() {
super.foo();
}
}
Object.setPrototypeOf(obj, proto);
obj.foo() // "world"
// 上面代碼中,super.foo指向原型對象proto的foo方法,但是綁定的this卻還是目前對象obj,是以輸出的就是world。
1.6 Object.keys(),Object.values(),Object.entries()
Object.keys
方法,傳回一個數組,成員是參數對象自身的(不含繼承的)所有可周遊(
enumerable
)屬性的鍵名。
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj) // ["foo", "baz"]
ES2017
引入了跟
Object.keys
配套的
Object.values
和
Object.entries
,作為周遊一個對象的補充手段,供
for...of
循環使用。
Object.values
方法傳回一個數組,成員是參數對象自身的(不含繼承的)所有可周遊(
enumerable
)屬性的鍵值。
const obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj) // ["b", "c", "a"]
上面代碼中,屬性名為數值的屬性,是按照數值大小,從小到大周遊的,是以傳回的順序是
b、c、a
。
Object.entries
方法傳回一個數組,成員是參數對象自身的(不含繼承的)所有可周遊(
enumerable
)屬性的鍵值對數組。
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj) // [ ["foo", "bar"], ["baz", 42] ]
除了傳回值不一樣,該方法的行為與
Object.values
基本一緻。
2 周遊
2.1 周遊方法
(1)
for...in
for...in
循環周遊對象自身的和繼承的可枚舉屬性(不含
Symbol
屬性)
(2)
Object.keys(obj)
Object.keys
傳回一個數組,包括對象自身的(不含繼承的)所有可枚舉屬性(不含
Symbol
屬性)的鍵名
(3)
Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames
傳回一個數組,包含對象自身的所有屬性(不含
Symbol
屬性,但是包括不可枚舉屬性)的鍵名。
(4)
Reflect.ownKeys(obj)
Reflect.ownKeys
傳回一個數組,包含對象自身的所有鍵名,不管鍵名是
Symbol
或字元串,也不管是否可枚舉。
2.2 周遊文法的比較
對于數組來說:
- for循環:最原始的寫法;
- forEach:無法中途跳出
循環,forEach
指令或break
指令都不能奏效;return
- for…in…:周遊可以獲得數組的鍵名,有以下缺點:數組的鍵是
,而周遊出來的鍵是字元串;number
循環不僅周遊數字鍵名,還會周遊手動添加其他鍵,甚至包括原型鍊上的鍵;在某些情況下,for...in
循環會以任意順序周遊鍵名。總之,for...in
循環主要是為了周遊對象而設計的,不适用于周遊數組。for...in
-
:周遊數組。有着同for...of
樣簡單文法,但是沒有for..in
那些缺點,不同于 for...in
方法,它可以與 forEach
、 break
和 continue
配合使用。提供了周遊所有資料結構的統一操作接口。return
對于對象來說:
周遊方式\屬性特點 | 是否可以周遊描述符為 的屬性 | 是否可以周遊自身的屬性 | 是否可以周遊原型上的屬性 |
| √ | √ | |
| √ | √ | |
| √ |
例如:
class Person { }
// 定義原型屬性 prototypeAttr
Person.prototype.prototypeAttr = 'prototypeAttr';
const jack = new Person('jack');
// 定義執行個體自身屬性 selfAttr
jack.selfAttr = 'selfAttr';
// 定義執行個體修飾符為 enumerable: false 的屬性
Object.defineProperty(jack, 'enumberableFalseAttr', {
value: 'enumberableFalseAttr',
enumerable: false,
writable: true,
configurable: true
使用
for...in...
周遊:
for (let key in jack) {
console.log(key);
}
=> selfAttr
=> prototypeAttr
Object.getOwnPropertyNames(jack)
=>['selfAttr','enumberableFalseAttr']
Object.keys(jack);
=> ['selfAttr']