原文地址:http://www.excelib.com/article/261/show
属性的描述也可以叫属性的特性,类似于对象的内部属性,主要作用就是描述属性自己的一些特征,他的表示方法和对象的内部属性一样,也是使用两个方括号来表示。对象的命名数据属性和命名访问器属性各有四个特性(内部属性没有),其中有两个特性是命名数据属性和命名访问器属性共同都有的,下面学生就分别给大家详细介绍。
命名数据属性的四个特性
命名数据属性的四个特性分别为:[[Value]]、[[Writable]]、[[Enumerable]]和[[Configurable]]。
[[Value]]表示属性的值;[[Writable]]表示属性值是否可以修改;[[Enumerable]]表示属性是否可枚举,如果为false就不会被for-in循环遍历到;[[Configurable]]表示属性是否可以被删除和属性的特性(除[[Value]]外)是否可修改。
属性的特性可以使用Object的getOwnPropertyDescriptor方法来查询,如果想修改的话可以使用Object的defineProperty和defineProperties方法,这两个方法操作的属性如果存在就会对其进行修改,否则就会创建。
我们来看个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | function log(msg){ console.log(msg); } var person = {name: "peter" }; log(Object.getOwnPropertyDescriptor(person, "name" )); // Object { configurable=true, enumerable=true, value="peter", writable=true} Object.defineProperty(person, "name" ,{writable: false }); //将person的name属性设置为不可修改 person.name = "maker" ; //修改无效 log(person.name); //peter Object.defineProperty(person, "age" ,{ //添加age属性 value:18, configurable: true }); log(Object.getOwnPropertyDescriptor(person, "age" )); // Object { configurable=true, enumerable=false, value=18, writable=false} log(Object.getOwnPropertyNames(person)); // ["name", "age"] for (prop in person){ //name:peter,因为age的enumerable为false,所以这里不会打印出age log(prop+ ":" +person[prop]); } Object.defineProperty(person, "age" , {writable: true }); //将person的age属性改为可修改 person.age = 21; log(person.age); //21 |
这个例子中我们定义了person对象,然后使用花括号定义了name属性以及使用defineProperty方法定义了age属性,使用Object.getOwnPropertyDescriptor可以看出使用花括号定义的属性默认[[Writable]]、[[Enumerable]]和[[Configurable]]都为true,而使用Object的defineProperty方法定义的属性如果没有明确声明的话[[Writable]]、[[Enumerable]]和[[Configurable]]默认值都为false,当[[Writable]]为false时就不可以修改属性的值了,[[Enumerable]]为false时for-in循环就遍历不到了,但是Object.getOwnPropertyNames方法依然可以获取到,[[Configurable]]属性如果为false就不可以使用defineProperty方法修改属性的特性了。
不过当[[Writable]]为false而[[Configurable]]为true时我们还可以使用defineProperty方法来修改属性的值,但是[[Configurable]]为false的时候就不可以修改了,比如下面的例子
1 2 3 4 5 6 7 8 9 10 11 | var obj = {}; Object.defineProperty(obj, "name" , {value: "peter" , configurable: true }); obj.name = "marcel" ; console.log(obj.name); //peter Object.defineProperty(obj, "name" , {value: "marcel" }); console.log(obj.name); //marcel Object.defineProperty(obj, "name" , {configurable: false }); Object.defineProperty(obj, "name" , {value: "peter" }); //抛出异常 |
这里使用defineProperty方法添加的name属性默认[[Writable]]为false所以不可以直接修改他的值,但是因为[[Configurable]]为true所以可以使用defineProperty方法通过[[Value]]来修改。当我们使用defineProperty方法将[[Configurable]]设置为false的时候如果再使用defineProperty方法就会抛出异常。当[[Configurable]]为false的时候属性也不可以使用delete删除。
[[enumerable]]特性可以使用propertyIsEnumerable方法来检查,这个方法是Object.prototype中的一个方法,所以一般对象都可以直接调用(create创建的prototype为null的对象除外),比如下面的例子
1 2 3 4 5 6 7 8 9 10 11 12 | var obj = {name: "peter" }; obj.job = "engineer" ; Object.defineProperty(obj, "age" , {value:21, enumerable: true }); Object.defineProperty(obj, "nationality" , {value: "USA" , enumerable: false }); Object.defineProperty(obj, "language" , {value: "english" }); console.log(obj.propertyIsEnumerable( "name" )); //true console.log(obj.propertyIsEnumerable( "job" )); //true console.log(obj.propertyIsEnumerable( "age" )); //true console.log(obj.propertyIsEnumerable( "nationality" )); //false console.log(obj.propertyIsEnumerable( "language" )); //false |
从这个例子中可以看出,使用花括号和点操作符创建的属性的[[enumerable]]特性默认为true,而使用defineProperty方法创建的属性如果没有明确声明的话[[enumerable]]默认为false。其他两个属性[[Configurable]]和[[Writable]]的默认值也是这样。
命名访问器属性的四个特性
命名访问器属性因为没有值所以也就没有[[Value]]特性,同时也就没有[[Writable]]特性,不过他比命名数据属性多了[[Get]]和[[Set]]特性,他们分别代表访问器属性的getter和setter方法。所以命名访问器属性也有四个特性:[[Get]]、[[Set]]、[[Enumerable]]和[[Configurable]],其中后两个特性和命名数据属性的含义是相同的,我们这里主要介绍他的前两个特性,我们先来看个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function log(msg){ console.log(msg); } var person = {_name: "peter" }; Object.defineProperty(person, "name" , { get: function () { log( "getting name" ); return this ._name; }, set: function (newName) { log( "name is changed to " + newName); this ._name = newName; } }); log(Object.getOwnPropertyDescriptor(person, "name" )); // Object { configurable=false, enumerable=false, get=function(), set=function() } person.name = "lucy" ; // name is changed to lucy log(person.name); // getting name, lucy |
这里我们使用Object的defineProperty方法给person对象添加了name访问器属性,其值保存在_name命名数据属性中,当我们获取name的值或者给name设置新值的时候就会调用相应的getter、setter方法,我们可以使用Object的getOwnPropertyDescriptor方法来获取name属性的所有特性。
另外,我们也可以在function中使用Object的defineProperty方法来给其创建出的对象实例添加属性,这时只要将对象写为this即可,而且这种方式还可以使用function的内部变量,比如我们将上面person对象改为由function类型的Person来创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function log(msg){ console.log(msg); } function Person(){ var name= "peter" ; Object.defineProperty( this , "name" , { get: function () { log( "getting name" ); return name; }, set: function (newName) { log( "name is changed to " + newName); name = newName; } }); } var person = new Person(); log(Object.getOwnPropertyDescriptor(person, "name" )); // Object { configurable=false, enumerable=false, get=function(), set=function() } person.name = "lucy" ; // name is changed to lucy log(person.name); // getting name, lucy |
这个例子中就在function中使用defineProperty方法创建了名为name的访问器属性,并在其中定义了getter和setter,也就是[[get]]和[[set]]特性,我们这里将他的值保存到了Person的局部变量name中,这样就可以屏蔽通过实例对象直接调用访问器属性的值了。