-
Notifications
You must be signed in to change notification settings - Fork 0
Description
es5 的Object.defineProperty() 用来给一个对象定义一个属性。vue的双向绑定原理就是基于defineProperty的访问器属性实现的。
使用语法
Object.defineProperty(obj, key, descriptor) 需要定义传入三个参数,这样就可以给对象obj定义一个key。
- obj: object类型,需要定义属性的对象
- key: string类型,属性的名称
- descriptor: object类型, 一个描述符,也是重点关注的参数
descriptor 参数
第三个参数是一个对象有六个键值。分别是:
- configurable(boolean, 默认false)
- enumerable(boolean, 默认false)
- writable(boolean, 默认false)
- value(any, 默认undefined)
- get(function, 默认undefined)
- set(function, 默认undefined)
其中writable, value任意一个和get, set任一一个都不可以同时存在。为什么呢?
关于红宝书上的介绍,一上来就是一段教科书的描述,比较晦涩。将defineProperty方法定义的属性分为两种类型:数据属性和访问器属性。 所以descriptor参数的不同决定了相应的属性的类型。
故descriptor有两种方式
描述数据属性(平时用obj.name = '123', 其实就是默认用这种方式定义的):
{
configurable: false, // 默认为false, 描述能否被改变,比如delete或者修改成访问器属性
enumerable: false, // 默认为false, 描述能否被 in 枚举,比如for-in
writable: false, // 默认false, 描述是否能被更改
value: [1, 2, 3], // 默认undefined, 就是属性的值
}
描述访问器属性
{
configurable: false, // 默认为false, 描述能否被改变,比如delete或者修改成访问器属性
enumerable: false, // 默认为false, 描述能否被 in 枚举,比如for-in
get: function() { return this.name }, // 默认undefined, getter函数
set: function(newVal) { this.name = newVal }, // 默认undefined, setter函数
}
简单实操
直接在chrome下进行:
var obj = {};
Object.defineProperty(obj, 'name', {
configurable: false, // 默认为false, 描述能否被改变,比如delete或者修改成访问器属性
enumerable: false, // 默认为false, 描述能否被for-in枚举
writable: false, // 默认false, 描述是否能被更改
value: 'wython' // 默认undefined, 就是属性的值
});
运行结果

可以看到是定义了一个属性name.这种方式和obj.name = 'wython'; 其实没啥区别。在这里面我把configrable, enumerable, writable改成false。
所以如果参数去改值,或者遍历, 都是不行的。
for(const i in obj) { console.log(i) } // 结果: undefined
delete obj.name; // 无效
obj.name = 'Another wython'; // 无效
尝试下访问器属性的定义方式
Object.defineProperty(obj, '_name', {
configurable: true,
enumerable: true,
get: function() { return this.name },
set: function(newVal) {
// 我们可以尝试更改name的值,因为是不可更改所以是无效的
this.name = newVal;
}
})
// 验证
obj._name = 1; // 无效,原因是访问器属性监听的是不可更改的name属性
所以说访问器属性监听的是另一个属性,如果监听的是自身,会报堆栈溢出的错误。
比如:
Object.defineProperty(obj, '_self', {
configurable: true,
enumerable: true,
get: function() { return this._self },
set: function(newVal) {
// 我们可以尝试更改name的值,因为是不可更改所以是无效的
this._self = newVal;
}
})
obj._self;
for-in
我们用for-in时,经常要加hasOwnProperty()的判断。这里提一个小疑问供读者思考,所有js类型都会继承至Object的原型,但是for-in是不会迭代出这些自带的属性的。为什么呢?实际上,我们hasOwnProperty判断也正是为了屏蔽默认定义的属性,如果我们用definePropery定义为enumerable: false。那就和原生的效果一样。有一件有趣事情,chrome控制台看到的浅色key和深色key。正是可for-in和不可for-in的关系。

总结
说实话,defineProperty方法平时几乎不用。毕竟现在工作上很多东西已经实现好了。我们可以用setter的这种方式触发notity。类似于观察者模式的方式监听属性,实现双向数据绑定的作用。小中见大,细节的东西也是很重要的。这个方法的兼容性是IE9. IE8下面有些问题。
