今天看《你不知道的JavaScript》第五章——原型的時候,注意到一個關於JavaScript屬性設置的有意思的地方。(P145)javascript
以前,我覺得除了對象被設置爲不可擴展的狀況,其餘狀況下給對象添加新屬性都會成功。但沒想到,還有其餘不能添加新屬性的狀況。我所說的這種狀況,就是原型鏈上有與你將要添加的屬性同名的屬性的時候。java
原型鏈上有與你將要添加的屬性同名的屬性的狀況,還要分紅三種狀況:this
原型鏈上有同名的數據屬性而且沒有被標記爲只讀,即writable: true
。code
原型鏈上有同名的數據屬性,但它被標記爲只讀,即writable: false
。對象
原型鏈上有同名的存取器屬性,且至少設置了setter
。ip
這裏以設置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的時候,就會很快將問題解決了。