JS中属性描述符

2020-10-18 09:48:46 浏览数 (1)

属性描述符是ES5中的一个重要的概念。它可以对对象做一些特定的高级操作,今天我们就学习一下ES5中的属性描述符。 ES5中的属性描述符是由Object类的一个静态方法defineProperty来设置的,该方法接收三个参数,分别是:属性操作的对象、属性名和一个属性描述符的对象。我们来看一个简单的例子:

代码语言:javascript复制
var obj = {
    a:"a"
};
Object.defineProperty(obj,"a",{
    value:"123"
});
console.log(obj.a);//123

这个例子中,我们使用属性描述符将对象obj的a属性的值设置为”123”。 属性描述符是由第三个参数来决定属性可以做哪些操作,这个对象可以设置以下的值:

描述符的值

描述

默认值

value

undefined

writable

是否可写

true

configurable

是否可配置

true

enumerable

是否可枚举

true

set

设置属性的函数

undefined

get

获取属性的函数

undefined

接下来我们一一简绍上面的这些值,由于value很简单,上面已经做过解释,就不再重复了。

1. writable

writable表示是否可写,如果其值设置为false,那么修改时会静默失败,严格模式下,会报错TypeError。

代码语言:javascript复制
var obj = {
    a:"a"
};
Object.defineProperty(obj,"a",{
    writable:false
});
obj.a = "123";//由于writable是false,这里会静默失败
console.log(obj.a);//a

2. configurable

configurable表示是否可配置,如果其值设置为false,那么将属性描述符重新设置的时候会报错TypeError(无论是否是在严格模式下);同时delete该属性的时候会静默失败,严格模式会报错TypeError。

代码语言:javascript复制
var obj = {
    a:"a"
};
Object.defineProperty(obj,"a",{
    configurable:false,
});
Object.defineProperty(obj,"a",{
    configurable:true,// 将configurable为false的重新开启会直接报错
    value:"111",
});
console.log(obj.a);

下面给出一个delete的例子:

代码语言:javascript复制
var obj = {
    a:"a"
};
Object.defineProperty(obj,"a",{
    configurable:false,
});
delete obj.a;// 这个地方会静默失败 严格模式下会报错
console.log(obj.a);//"a"

configurable有三点需要注意的: 2.1 所谓的不可配置是不能修改,如果重新设置相同的属性描述符是不会报错的:

代码语言:javascript复制
var obj = {
    a:"a"
};
Object.defineProperty(obj,"a",{
    configurable:false
});
Object.defineProperty(obj,"a",{
    configurable:false// 这个地方并不会报错 因为属性描述符并没有改变
});
console.log(obj.a);//"a"

2.2 如果configurable为false的时候仍然可以把writable从true改成false,但是不能把writable从false修改成true。

代码语言:javascript复制
var obj = {
    a:"a"
};
Object.defineProperty(obj,"a",{
    configurable:false,
    writable:true
});
Object.defineProperty(obj,"a",{
    writable:false // writable从true设置为false是可以的
});
obj.a = "b";// writable为false 静默失败
console.log(obj.a);//"a"

2.3 如果configurable为false并且writable从true的时候,那么修改value是可以的。

代码语言:javascript复制
var obj = {
    a:"a"
};
Object.defineProperty(obj,"a",{
    configurable:false,
    writable:true,
    value:"111"
});
Object.defineProperty(obj,"a",{
    value:"222" // value其实是由writable来决定的
});
console.log(obj.a);//"222"

3. enumerable

enumerable表示是否可枚举,如果设置为false,那么for-in中获取不到该值。

代码语言:javascript复制
var obj = {
    a:"a",
    b:"b",
    c:"c"
};
Object.defineProperty(obj,"b",{
    enumerable:false
});
for (var key in obj) {
    console.log(obj[key]);// 依次打印"a" "c"
}

通常判断一个属性是否属于一个对象我们可以使用in操作符和hasOwnProperty方法,对于不可枚举的属性,他们返回的都是true,如上面的不可枚举的属性b:

代码语言:javascript复制
console.log("b" in obj);// true
console.log(obj.hasOwnProperty("b"));// true

那么如何区分某个属性是不可枚举的呢?可以利用对象的propertyIsEnumerable方法。

代码语言:javascript复制
console.log(obj.propertyIsEnumerable("b"));// false

如果要获取对象的所有属性(键),那怎么办?可以使用下面两个方法:

代码语言:javascript复制
console.log(Object.keys(obj));// 获取所有可枚举的属性 结果是 ["a", "c"]
console.log(Object.getOwnPropertyNames(obj));// 获取所有的属性,不管是否可以枚举 结果是 ["a", "b", "c"]

4. set和get

set和get通常是方法,分别定义了设置值和获取值是的逻辑,我们这里给出一个例子,当设置了一个数值读取的时候返回这个数值的平方:

代码语言:javascript复制
var obj = {
};
Object.defineProperty(obj,"a",{
    set : function (_a){
        this._a = _a;//这个地方必须注意 这里使用的是_a而不是a 因为如果是a的话会陷入死循环
    },
    get : function (){
        return this._a * this._a;
    }
});
obj.a = 4;//这里会调用set方法
console.log(obj.a);// 这里会调用get方法 打印16

set和get也可以定义在对象上,而不使用属性描述符,如下:

代码语言:javascript复制
var obj = {
    set a(_a){//这个地方和函数属性的写法有一点点的区别
        this._a = _a;
    },
    get a(){
        return this._a * this._a;
    }
};
obj.a = 4;//这里会调用set方法
console.log(obj.a);// 这里会调用get方法 打印16

那么问题来了,如果同一次设置属性描述符中既有get和set又有value那么会以哪个为准呢?这种情况下浏览器就会报错,也就是不允许这么做。 如果多次设置同一个属性的属性描述符那么后面的会覆盖前面的。

代码语言:javascript复制
var obj = {
    a:123
};
Object.defineProperty(obj,"a",{
    set :function (_a){
        this._a = _a;
    },
    get :function (){
        return this._a * this._a;
    },
});
obj.a = 4;
console.log(obj.a);// 16

Object.defineProperty(obj,"a",{
    writable:true,
    value:321
});

console.log(obj.a);// 321

补充

  1. 在调用Object.defineProperty方法创建一个新的属性的时候,如果不指定writableconfigurableenumerable的时候默认值是false,如果只是修改已定义的属性的时候那么就是默认值true。
代码语言:javascript复制
var obj = {
    a:"111"
};
Object.defineProperty(obj,"a",{
    value:"a"
});
Object.defineProperty(obj,"b",{
    value:"b"
});
console.log(Object.keys(obj));//b是新的 所以是不可枚举的 所以打印["a"]

2. 获取属性描述符,可以使用Object.getOwnPropertyDescriptor方法。

代码语言:javascript复制
Object.getOwnPropertyDescriptor(obj,"a");

3. 批量设置多个属性描述符的时候,可以使用Object.defineProperties方法。

代码语言:javascript复制
Object.defineProperties(obj,{
    a:{value:"aaa",writable:false},
    b:{value:"bbb",writable:false},
});

0 人点赞