ECMAScript 中有两种属性:数据属性和访问器属性。
数据属性
数据属性有4个描述其行为的特性:
属性都是用两个方括号定义的,比如上面的value,其实是[[value]]。[[Configurable]]为false不能通过delete删除其属性。
例子:
var person = {
name: "pyc"
};
比如上面这段代码,[[value]]特性将被设置为'pyc',其他的特性都默认变为true,这就是对象表达式的默认。
有图有真相。这里的对象函数ECMAScript 5 的Object.getOwnPropertyDescriptor()就是获取给定属性的描述。接收两个参数:属性所属对象(name所属对象为person)和要读取其描述符的属性名称(name)。当然这是ECMAScript 5的方法,兼容性IE9+。
既然上面一定义属性就有默认特性,如果要修改某些特性值呢?
ECMAScript 5 的 Object.defineProperty()方法,就用来干这件事(vue框架里面经常用到哦,响应式变化。虽然据说3.0要用Proxy来代替,后面再说吧)。
三个参数:属性所在对象,属性的名字和一个描述符对象。(在谁上面改,改哪个属性,改动点是什么)。拿上面的继续举例:
Object.defineProperty(person, "name", {
writable: false,
value: "pyc"
});
console.log(person.name); //"pyc"
person.name = "Greg";
console.log(person.name); //"pyc"
其他属性根据各自的作用也是一样的。比如configurable设为false,那么就不能delete删除属性,否则会被忽略,严格模式下报错
最上面我们定义name属性通过对象字面量。如果我们通过Object.defineProperty()方法来定义属性,那么如果里面的值没指定就是上面的表格里的默认值。
访问器属性
访问器属性同样有四个特性:
最前面两个和数据属性是一样的。
还是上面的person例子,这是时候我们先用对象字面量加一个age属性。然后用定义方法加一个year属性。
person.age = 23;
Object.defineProperty(book, "year", {
get: function(){
return this._year;
},
set: function(newValue){
if (newValue < 1995) {
this.age += 1995 - year;
}
}
});
当我们获取year这个属性的时候,就会调用get方法,同理,设置year属性的时候,就会调用set方法,设置的值就会传递个newValue。set和get不一定都要指定,只是说指定其中一个另一个不可用。
有人就要问了,你这都是定义一个属性,那么我同时要设置多个属性的特性怎么弄呢?(懒惰是最大的生产力)
ECMAScript 5 又定义了一个 Object.defineProperties()方法。
接收两个参数:要修改属性所在的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。例如:
Object.defineProperties(person, {
interest: {
value: 'music'
}
});
等等,一次列举就好。
其实明白一点:上面说的所有东西,其实都是对一个对象属性的描述。比如一个name属性,可不可读呢,可不可删呢之类的。
一些特性说明:
[[Configurable]]
关于这个特性,有几个需要注意的地方。
1、使用var生命的变量,此特性默认为false。看下图
这也是为什么全局变量不能通过delete删除,而直接在window对象上的定义属性就可以。(特性这些小妖在作怪)
2、一旦此特性设置为false,再修改除writable之外的属性都会报错
[[value]]
只要 writable 和 configurable 有一个为 true,就允许改动。
[[writable]]
writable 只有在从 false 改为 true 会报错,从 true 改为 false 则是允许的。
最后:一个属性只能是数据属性或者访问器属性,所以均拥有 [[Configurable]] 和 [[Enumerable]] 两个特性,但是对于其他两个属性,则分别拥有,所以不可能同时有 [[Value]] 和 [[Set]],[[Writable]] 和 [[Set]] 等(如果同时定义则会报错)。
参考资料:
https://msdn.microsoft.com/zh-cn/library/hh965578(v=vs.94).aspx