对象属性描述符

对于操作系统中的文件,我们可以驾轻就熟将其设置为只读、隐藏、系统文件或普通文件。

于对象来说,属性描述符提供类似的功能,用来描述对象的值、是否可配置、是否可修改以及是否可枚举。

描述符类型:

对象属性描述符的类型分为两类:数据属性访问器属性

数据属性

数据属性包含一个数据值的位置,在这个位置可以读取和写入值。

数据属性有4个特性:

【1】Configurable(可配置性)

【2】Enumerable(可枚举性)

【3】Writable(可写性)

【4】Value(属性值)

属性值 包含这个属性的数据值,读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。默认值为undefined

访问器属性

对象属性是名字、值和一组属性描述符构成的。而属性值可以用一个或两个方法替代,这两个方法就是getter和setter。而这种属性类型叫访问器属性:

【1】Configurable(可配置性)

【2】Enumerable(可枚举性)

【3】getter

【4】setter

和数据属性不同,访问器属性不具有可写性(Writable)。

如果属性同时具有getter和setter方法,那么它是一个读/写属性;如果它只有getter方法,那么它是一个只读属性;如果它只有setter方法,那么它是一个只写属性。读取只写属性总是返回undefined


描述符方法

前面介绍了属性描述符,要想设置它们,就需要用到描述符方法。描述符方法总共有以下4个:

【1】Object.getOwnPropertyDescriptor()

【2】Object.defineProperty()

【3】Object.defineProperties()

【4】Object.create()

Object.getOwnPropertyDescriptor()

Object.getOwnPropertyDescriptor(o,name)方法用于查询一个属性的描述符,并以对象的形式返回:

var obj = {a:1};
//Object {value: 1, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(obj,'a'));
//undefined
console.log(Object.getOwnPropertyDescriptor(obj,'b'));

查询obj.a属性时,可配置性、可枚举性、可写性都是默认的true,而value是a的属性值1

查询obj.b属性时,因为obj.b属性不存在,该方法返回undefined

Object.defineProperty()

Object.defineProperty(o,name,desc)方法用于创建或配置对象的一个属性的描述符,返回配置后的对象。

使用该方法创建或配置对象属性的描述符时,如果不针对该属性进行描述符的配置,则该项描述符默认为false:

var obj = {};
//{a:1}
console.log(Object.defineProperty(obj,'a',{
        value:1,
        writable: true
    }));

//由于没有配置enumerable和configurable,所以它们的值为false
//{value: 1, writable: true, enumerable: false, configurable: false}
console.log(Object.getOwnPropertyDescriptor(obj,'a'));

Object.defineProperties()

Object.defineProperty(o,descriptors)方法用于创建或配置对象的多个属性的描述符,返回配置后的对象:

var obj = {
    a:1
};
//{a: 1, b: 2}
console.log(Object.defineProperties(obj,{
        a:{writable:false},
        b:{value:2}
    }));

//{value: 1, writable: false, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(obj,'a'));
//{value: 2, writable: false, enumerable: false, configurable: false}
console.log(Object.getOwnPropertyDescriptor(obj,'b'));

Object.create()

Object.create(proto,descriptors)方法使用指定的原型和属性来创建一个对象

var o = Object.create(Object.prototype,{
    a:{writable: false,value:1,enumerable:true}
});
//{value: 1, writable: false, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(obj,'a'));

描述符–详细说明

前面分别介绍了数据属性和访问器属性的描述符,但没有详细说明其含义及使用,接下来逐一进行说明:

可写性(writable)

可写性决定是否可以修改属性的值,默认值为true;设置writable:false后,赋值语句会静默失效:

var o = {a:1};
Object.defineProperty(o,'a',{
    writable:false
});
console.log(o.a);//1
//由于设置了writable为false,所以o.a=2这个语句会静默失效
o.a = 2;
console.log(o.a);//1
Object.defineProperty(o,'a',{
    writable:true
});
//由于writable设置为true,所以o.a可以被修改为2
o.a = 2;
console.log(o.a);//2

设置writable:false后,能通过Object.defineProperty()方法改变属性value的值,这过程不会影响到writable的值,因为这也意味着再重置writable的属性值为false:

var o = {a:1};
Object.defineProperty(o,'a',{
    writable:false
});
console.log(o.a);//1
Object.defineProperty(o,'a',{
    value:2
});
console.log(o.a);//2,writable依然是false

严格模式下通过赋值语句为writable为false的属性赋值,会提示类型错误TypeError:

'use strict';
var o = {a:1};
Object.defineProperty(o,'a',{
    writable:false
});
//Uncaught TypeError: Cannot assign to read only property 'a' of object '#<Object>'
o.a = 2;

可配置性(Configurable)

可配置性决定是否可以使用delete删除属性,以及是否可以修改属性描述符的特性,默认值为true

【1】设置Configurable:false后,无法使用delete删除属性

var o = {a:1};
Object.defineProperty(o,'a',{
    configurable:false
});
delete o.a;//false
console.log(o.a);//1

使用var命令声明变量时,变量的configurable为false:

var a = 1;
//{value: 1, writable: true, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(this,'a');

在严格模式下删除为configurable为false的属性,会提示类型错误TypeError

'use strict';
var o = {a:1};
Object.defineProperty(o,'a',{
    configurable:false
});
//Uncaught TypeError: Cannot delete property 'a' of #<Object>
delete o.a;

【2】一般地,设置Configurable:false后,将无法再使用defineProperty()方法来修改属性描述符

var o = {a:1};
Object.defineProperty(o,'a',{
    configurable:false
});
//Uncaught TypeError: Cannot redefine property: a
Object.defineProperty(o,'a',{
    configurable:true
});

有一个例外,设置Configurable:false后,只允许writable的状态从true变为false

var o = {a:1};
Object.defineProperty(o,'a',{
    configurable:false,
    writable:true
});
o.a = 2;
console.log(o.a);//2
Object.defineProperty(o,'a',{
    writable:false
});
//由于writable:false生效,对象a的o属性无法修改值,所以o.a=3的赋值语句静默失败
o.a = 3;
console.log(o.a);//2

可枚举性(Enumerable)

可枚举性决定属性是否出现在对象的属性枚举中,具体来说就是,for-in循环、Object.keys方法、JSON.stringify方法是否会取到该属性。

用户定义的普通属性默认是可枚举的,而原生继承的属性默认是不可枚举的

//由于enumerable被设置为false,在for-in循环中a属性无法被枚举出来
var o = {a:1};
Object.defineProperty(o,'a',{enumerable:false});
for(var i in o){
    console.log(o[i]);//undefined
}

propertyIsEnumerable()

propertyIsEnumerable()方法用于判断对象的属性是否可枚举

var o = {a:1};
console.log(o.propertyIsEnumerable('a'));//true
Object.defineProperty(o,'a',{enumerable:false});
console.log(o.propertyIsEnumerable('a'));//false

get和set

get是一个隐藏函数,在获取属性值时调用。set也是一个隐藏函数,在设置属性值时调用,它们的默认值都是undefined。

Object.definedProperty()中的get和set对应于对象字面量中get和set方法。

getter和setter取代了数据属性中的value和writable属性。

【1】一般地,set和get方法是成对出现的:

var o ={
    get a(){
        return this._a;
    },
    set a(val){
        this._a = val*2;
    }
}
o.a = 1;
console.log(o.a);//2

Object.definedProperty()中的get和set对应于对象字面量中get和set方法:

Object.defineProperty(o,'a',{
    get: function(){
        return this._a;
    },
    set :function(val){
        this._a = val*2;
    }
})
o.a = 1;
console.log(o.a);//2

【2】给只设置get方法,没有设置set方法的对象赋值会静默失败(在严格模式下会报错),所以为了规范起见最好同时设置getter和setter:

var o = {
    get a(){
        return 2;
    }
}    
console.log(o.a);//2
//由于没有设置set方法,所以o.a=3的赋值语句会静默失败
o.a = 3;
console.log(o.a);//2

同样地:

Object.defineProperty(o,'a',{
    get: function(){
        return 2;
    }
})
console.log(o.a);//2
//由于没有设置set方法,所以o.a=3的赋值语句会静默失败
o.a = 3;
console.log(o.a);//2

在严格模式下,给没有设置set方法的访问器属性赋值会报错:

'use strict';
var o = {
    get a(){
        return 2;
    }
}    
console.log(o.a);//2
//由于没有设置set方法,所以o.a=3的赋值语句会报错
//Uncaught TypeError: Cannot set property a of #<Object> which has only a getter
o.a = 3;

并且在Object.definedProperty()中也是同理。


【3】只设置set方法,而不设置get方法,则对象属性值为undefined:

var o = {
    set a(val){
        return 2;
    }
}    
o.a = 1;
console.log(o.a);//undefined

同样地:

Object.defineProperty(o,'a',{
    set: function(){
        return 2;
    }
})
o.a = 1;
console.log(o.a);//undefined

// 相关话题:

【1】对象属性的操作: 链接

属性操作可分为属性查询、属性设置、属性删除,还包括属性继承。

【2】对象属性描述符: “内容即本文。”

属性描述符用来描述对象的值、是否可配置、是否可修改以及是否可枚举。

【3】对象状态: 链接

属性描述符只能用来控制对象中一个属性的状态,而如果要控制整个对象的状态,就要用到6种对象状态。

文章目录
  1. 数据属性
  2. 访问器属性
  3. 描述符方法
    1. Object.getOwnPropertyDescriptor()
    2. Object.defineProperty()
    3. Object.defineProperties()
    4. Object.create()
  4. 描述符–详细说明
    1. 可写性(writable)
    2. 可配置性(Configurable)
    3. 可枚举性(Enumerable)
      1. propertyIsEnumerable()
    4. get和set
  5. // 相关话题: