天天看點

Object屬性的可枚舉性和周遊方法

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      

【注意】:

  1. 如果第一個參數不是對象,會自動轉為對象。但是由于傳回的還是第一個參數,是以這個操作不會産生任何效果。
  2. 由于​

    ​undefined​

    ​​和​

    ​null​

    ​​無法轉為對象,是以如果第一個參數是​

    ​undefined​

    ​​或​

    ​null​

    ​,就會報錯。

1.4 Object.getPrototypeOf()

該方法與​

​Object.setPrototypeOf​

​方法配套,用于讀取一個對象的原型對象。

Object.getPrototypeOf(obj);      

【注意】:

  1. 如果參數不是對象,會被自動轉為對象。
  2. 如果參數是​

    ​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 周遊文法的比較

對于數組來說:

  1. for循環:最原始的寫法;
  2. forEach:無法中途跳出​

    ​forEach​

    ​​循環,​

    ​break​

    ​​指令或​

    ​return​

    ​指令都不能奏效;
  3. for…in…:周遊可以獲得數組的鍵名,有以下缺點:數組的鍵是​

    ​number​

    ​,而周遊出來的鍵是字元串;​

    ​for...in​

    ​​循環不僅周遊數字鍵名,還會周遊手動添加其他鍵,甚至包括原型鍊上的鍵;在某些情況下,​

    ​for...in​

    ​​循環會以任意順序周遊鍵名。總之,​

    ​for...in​

    ​循環主要是為了周遊對象而設計的,不适用于周遊數組。
  4. ​for...of​

    ​​:周遊數組。有着同​

    ​for..in​

    ​​樣簡單文法,但是沒有​

    ​for...in​

    ​​那些缺點,不同于 ​

    ​forEach ​

    ​​方法,它可以與 ​

    ​break​

    ​​ 、 ​

    ​continue ​

    ​​和 ​

    ​return​

    ​ 配合使用。提供了周遊所有資料結構的統一操作接口。

對于對象來說:

周遊方式\屬性特點 是否可以周遊描述符為​

​enumberable:false​

​的屬性
是否可以周遊自身的屬性 是否可以周遊原型上的屬性

​for...in... ​

​Object.getOwnPropertyNames​

​Object.keys​

例如:

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']      

繼續閱讀