一、引入
从 ES5 开始,所有的属性都具备了属性描述符。
var myObject = {
a: 2
}
Object.getOwnPropertyDescriptor(myObject, 'a')
// {value: 2, writable: true, enumerable: true, configurable: true}
这个普通的对象属性对应的属性描述符可不仅仅只是一个 2。它还包含另外三个特性,writable(不可写)、enumerable(可枚举)、configurable(可配置)。在创建普通属性时属性描述符会使用默认值。我们可以使用 Object.defineProperty(…) 来添加一个新属性或者修改一个已有属性
var myObject = {};
Object.defineProperty(myObject, 'a', {
value: 2,
writable: true,
configurable: true,
enumerable: true
})
我们使用 defineProperty(…)添加了一个普通的属性,然而,一般来说不会使用这种方式添加,除非你想修改属性描述符
二、各个属性描述符的解释和使用
2.1 writable
writable决定是否可以修改属性的值
var myObject = {};
Object.defineProperty(myObject, 'a', {
value: 2,
writable: false, // 不可写
configurable: true,
enumerable: true
})
myObject.a = 3;
myObject.a; // 2
我们对于属性值的修改失败了,如果在严格模式下,会出错,TypeError
2.2 configurable
只要属性是可配置的,就可以使用 defineProperty(…) 方法来修改属性描述符。
var myObject = {
a: 2
}
myObject.a = 3;
myObject.a; // 3
Object.defineProperty(myObject, 'a', {
value: 4,
writable: true,
configurable: false, // 不可配置
enumerable: true
})
myObject.a; // 4
myObject.a = 5; //可以修改值
myObject.a; // 5
Object.defineProperty(myObject, 'a', {
value: 6
})
myObject.a; // 6
Object.defineProperty(myObject, 'a', {
writable: false
})// 不报错
myObject.a = 7;
myObject.a // 6
Object.defineProperty(myObject, 'a', {
writable: true
})
// Uncaught TypeError: Cannot redefine property: a
// at Function.defineProperty (<anonymous>)
// at <anonymous>:2:8
Object.defineProperty(myObject, 'a', {
configurable: true
}) //TypeError
Object.defineProperty(myObject, 'a', {
enumerable: false
}) //TypeError
不管是不是处于严格模式,修改一个不可配置的属性描述符都会出错,把 configurable 修改成 false 是单向操作,无法撤销.
即便属性是 configurable:false,还是可以把 writable 改成 false,但是无法由 false 改成 true,这是个例外。
configuable:fase 还会禁止删除这个属性
var myObject = {};
Object.defineProperty(myObject, 'a', {
value: 4,
writable: true,
configurable: false, // 不可配置
enumerable: true
})
delete myObject.a;
myObject.a; // 2
2.3 enumerable
这个描述符控制的是属性是否出现在对象的属性枚举中,比如 for…in 循环,如果把 enumerable 设置成 false,这个属性就不会出现在枚举中。
2.4 get 和 set 函数
除了使用点语法或中括号语法访问属性的 value 外,还可以使用访问器,包括 set 和 get 两个函数。其中,set() 函数可以设置 value 属性值,而 get() 函数可以读取 value 属性值。
借助访问器,可以为属性的 value 设计高级功能,如禁用部分特性、设计访问条件、利用内部变量或属性进行数据处理等。
2.4.1 示例1
下面示例设计对象 obj 的 x 属性值必须为数字。为属性 x 定义了 get 和 set 特性,obj.x 取值时,就会调用 get;赋值时,就会调用 set。
var obj = Object.create(Object.prototype, {
_x : { //数据属性
value : 1, //初始值
writable : true
},
x : { //访问器属性
get : function () { //getter
return this._x; //返回_x属性值
},
set : function (value) { //setter
if (typeof value != "number") throw new Error('请输入数字');
this._x = value; //赋值
}
}
});
console.log(obj.x); //1
obj.x = "2"; //抛出异常
2.4.2 示例2
JavaScript 也支持一种简写方法。针对示例 1,通过以下方式可以快速定义属性。
var obj = {
_x : 1, //定义_x属性
get x() { return this._x }, //定义x属性的getter
set x(value) { //定义x属性的setter
if (typeof value != "number") throw new Error('请输入数字');
this._x = value; //赋值
}
};
console.log(obj.x); //1
obj.x = 2;
console.log(obj.x); //2
注意:JavaScript的set 和get中不能访问自己,会导致递归死循环
var obj = {
x : 1, //定义_x属性
get x() { return this.x }, //定义x属性的getter
set x(value) { //定义x属性的setter
if (typeof value != "number") throw new Error('请输入数字');
this.x = value; //赋值
}
};
obj.x //Uncaught RangeError: Maximum call stack size exceeded