一個你可能沒有注意到的關於JavaScript屬性設置的知識

今天看《你不知道的JavaScript》第五章——原型的時候,注意到一個關於JavaScript屬性設置的有意思的地方。(P145javascript

以前,我覺得除了對象被設置爲不可擴展的狀況,其餘狀況下給對象添加新屬性都會成功。但沒想到,還有其餘不能添加新屬性的狀況。我所說的這種狀況,就是原型鏈上有與你將要添加的屬性同名的屬性的時候。java

分類

原型鏈上有與你將要添加的屬性同名的屬性的狀況,還要分紅三種狀況:this

  • 原型鏈上有同名的數據屬性而且沒有被標記爲只讀,即writable: truecode

  • 原型鏈上有同名的數據屬性,但它被標記爲只讀,即writable: false對象

  • 原型鏈上有同名的存取器屬性,且至少設置了setterip

規則

這裏以設置myObject.foo = 'my'爲例。原型鏈

同名數據屬性、可讀

若是在[[Prototype]]鏈上層存在名爲foo的普通數據訪問屬性而且沒有被標記爲只讀(wirtable: false),那麼就會在myObject中添加一個名爲foo的新屬性,它就是屏蔽屬性。get

這種狀況是最多見的,下面貼一個簡單的例子。原型

var proObject = {
    foo: 'pro'
}
var myObject = Object.create(proObject)
myObject.foo = 'my'
myObject.foo                      // 'my'
myObject.hasOwnProperty('foo')    // true

同名數據屬性、只讀

若是在[[Prototype]]鏈上層存在foo,可是它被標記爲只讀(writable: false),那麼沒法修改已有屬性或者在myObject上建立屏蔽屬性。若是運行在嚴格模式下,代碼會拋出一個錯誤。不然,這條賦值語句會被忽略。博客

var proObject = {}
Object.defineProperty(proObject, 'foo', {
    value: 'pro',
    wirtable: false
})
var myObject = Object.create(proObject)
myObject.foo = 'my'
myObject.foo                      // 'pro'
myObject.hasOwnProperty('foo')    // false

'use strict'
myObject.foo = 'my'    // Uncaught TypeError: Cannot assign to read only property 'foo' of object '#<Object>'

同名的存取器屬性

若是在[[Prototype]]鏈上層存在foo而且它是一個setter,那就必定會調用這個setter。foo不會被添加到(或者說屏蔽於)myObject,也不會從新定義foo這個setter。

var proObject = {}
Object.defineProperty(proObject, 'foo', {
    set: function(val) {
        this.s = val
    },
    get: function() {
        return this.s
    }
})
myObject.foo = 'my'
myObject.hasOwnProperty('foo')    // false

// 能夠看到存取器屬性沒有被從新定義
Object.getOwnPropertyDescriptor(proObject, 'foo')

解決方案

若是你但願在上述的第二和第三中狀況下爲myObject添加新屬性的話,你須要使用Object.defineProperty或者Object.getOwnPropertyDescriptors來添加新屬性。

結語

終於在週日完成了這周的博客文章了(雖然很無恥地「水了一篇」,但好歹也算一篇文章嘛。)

正經一點!!!JavaScript中還有不少讓咱們出乎意料的地方,雖然平時不多遇到這些方面知識的應用,但一旦踩了這些坑,仍是會耗掉咱們挺多時間和精力的。因此,咱們平時應該多留意這些知識,並積累下來。那麼當咱們遇到關於這些知識的bug的時候,就會很快將問題解決了。

相關文章
相關標籤/搜索