通过Object.defineProperty()我们可以给Object对象定义属性,同时还能更细粒度的控制该属性。
基本语法:
Object.defineProperty(obj, prop, descriptor)
参数:
- obj是要定义属性的对象
- prop是要定义的属性的名称或Symbol
- descriptor是定义属性的描述符,描述符有两种形式,数据描述符和存取描述符
返回值:返回第一个参数obj
数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。一个描述符只能是这两者其中之一,但两个不能同时使用。
上手
var obj = {};
Object.defineProperty(obj, 'foo', {
value: 'hello',
writable: true
});
console.log(obj); // {foo: "hello"}
Object.defineProperty(obj, 'bar', {
get: function() {
return 'world';
}
})
console.log(obj.bar); // world
上面代码我们分别使用了不同的描述符定义属性,用起来就是这么简单,描述符中我们还能配置属性的其他选项,其中两个描述符共享configurable
和enumerable
选项:
- configurable:当为true时,属性的描述符才能够被更改,同时该属性也能被删除,默认false
- enumerable:当为true时,属性才能被枚举到,默认false
数据描述符有value
和writable
选项:
- value:该属性对应的值,可以是数值,对象,函数等,默认
undefined
,只能用于数据描述符 - writable:当为true时,才能使用赋值操作改变上面value的值,默认false
存取描述符有get
和set
选项:
- get:是一个无参的
getter
函数,访问该属性时会调用这个函数,并且会传入this
对象,函数的返回值作为属性值,如果没有get
,就会返回undefined
- set:当修改属性值时,这个函数会被调用,并且新的值会被作为参数传进来,同时this对象指向被赋值的对象
汇总如下:
描述符不能混用,即一个描述符同时拥有value
或writable
和get
或 set
选项,否则会产生一个异常:
var obj = {};
Object.defineProperty(obj, 'bar', {
value: 'hello',
get: function() {
return 'world';
}
});
Uncaught TypeError: Invalid property descriptor.
Cannot both specify accessors and a value or writable attribute
我们通过几个例子看看enumerable
在不同枚举方法中的表现:
var obj = {}
Object.defineProperty(obj, 'a', {
value: 1,
enumerable: true
});
Object.defineProperty(obj, 'b', {
value: 2,
enumerable: false
});
Object.defineProperty(obj, 'c', {
value: 3, // enumerable默认为false
});
obj.d = 4 // enumerable默认为true
Object.defineProperty(obj, Symbol.for('e'), {
value: 5,
enumerable: true
});
Object.defineProperty(obj, Symbol.for('f'), {
value: 6,
enumerable: false
});
在for…in
中:
for (var i in obj) {
console.log(i); // a, d
}
属性名是symbol的不能被打印出来,enumerable是false也无法打印
在Object.keys()
中:
Object.keys(obj); // ["a", "d"]
和上面一样
在展开操作符…
中:
var foo = {...obj}
console.log(foo); // {a: 1, d: 4, Symbol(e): 5}
enumerable为true的都能被解构出来,symbol也可以
通过点操作符和Object.defineProperty()添加属性有什么区别:
var obj = {}
obj.foo = 2
// 等价于
Object.defineProperty(obj, 'foo', {
value: 1,
writable: true,
configurable: true,
enumerable: true
});