对象属性描述符

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

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

描述符类型:

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

数据属性

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

数据属性有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. 1. 数据属性
  2. 2. 访问器属性
  3. 3. 描述符方法
    1. 3.1. Object.getOwnPropertyDescriptor()
    2. 3.2. Object.defineProperty()
    3. 3.3. Object.defineProperties()
    4. 3.4. Object.create()
  4. 4. 描述符–详细说明
    1. 4.1. 可写性(writable)
    2. 4.2. 可配置性(Configurable)
    3. 4.3. 可枚举性(Enumerable)
      1. 4.3.1. propertyIsEnumerable()
    4. 4.4. get和set
  5. 5. // 相关话题:
|