今天看《你不知道的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的时候,就会很快将问题解决了。