首页
属性的描述
属性的描述
版权声明:本文为原创内容,转载请声明出处。
原文地址:http://www.excelib.com/article/261/show

属性的描述也可以叫属性的特性,类似于对象的内部属性,主要作用就是描述属性自己的一些特征,他的表示方法和对象的内部属性一样,也是使用两个方括号来表示。对象的命名数据属性和命名访问器属性各有四个特性(内部属性没有),其中有两个特性是命名数据属性和命名访问器属性共同都有的,下面学生就分别给大家详细介绍。

命名数据属性的四个特性

命名数据属性的四个特性分别为:[[Value]][[Writable]][[Enumerable]][[Configurable]]

[[Value]]表示属性的值;[[Writable]]表示属性值是否可以修改;[[Enumerable]]表示属性是否可枚举,如果为false就不会被for-in循环遍历到;[[Configurable]]表示属性是否可以被删除和属性的特性(除[[Value]]外)是否可修改。

属性的特性可以使用ObjectgetOwnPropertyDescriptor方法来查询,如果想修改的话可以使用ObjectdefinePropertydefineProperties方法,这两个方法操作的属性如果存在就会对其进行修改,否则就会创建。

我们来看个例子

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,而使用ObjectdefineProperty方法定义的属性如果没有明确声明的话[[Writable]][[Enumerable]][[Configurable]]默认值都为false,当[[Writable]]false时就不可以修改属性的值了,[[Enumerable]]falsefor-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创建的prototypenull的对象除外),比如下面的例子

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]]特性,他们分别代表访问器属性的gettersetter方法。所以命名访问器属性也有四个特性:[[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

这里我们使用ObjectdefineProperty方法给person对象添加了name访问器属性,其值保存在_name命名数据属性中,当我们获取name的值或者给name设置新值的时候就会调用相应的gettersetter方法,我们可以使用ObjectgetOwnPropertyDescriptor方法来获取name属性的所有特性。

另外,我们也可以在function中使用ObjectdefineProperty方法来给其创建出的对象实例添加属性,这时只要将对象写为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的访问器属性,并在其中定义了gettersetter,也就是[[get]][[set]]特性,我们这里将他的值保存到了Person的局部变量name中,这样就可以屏蔽通过实例对象直接调用访问器属性的值了。