Object.defineProperty是es5中新增加的属性描述符。在它出现之前你可能会经常有一些疑问,比如for in 循环为何不能遍历出函数的arguments、length、name等属性名,delete window.a 为何返回false, 等你学完这个api,这些现象便都能够得到解释。
Object.defineProperty 一共涉及6个可配置项:
writable: 是否可重写
value: 当前值
get: 读取时内部调用的函数
set: 写入时内部调用的函数
enumerable: 是否可以遍历
configurable: 是否可再次修改配置项
我们先试图定义一个简单的对象如下:
var obj = {x:1}
看上去非常简单的一句代码,但它的定义细节比我们看到的要复杂的多,上边这句代码可以转换为es5的以下定义:
var obj = Object.create(
Object.prototype,{
x: {
value:1,
writable:true,
enumerable: true,
configurable: true
}
}
)
实际上在es3中也有 [[ReadOnly]] 、 [[DontEnum]]、 [[DontDelete]] 这样的数据属性,对应了es5 中的访问器属性 [[writable]] 、 [[Enumerable]] 、[[Configurable]]
下面来看下Object.defineProperty的具体用法:
Object.defineProperty方法有三个参数,第一个参数为要定义或修改的对象-object。第二个参数为对象的属性名-string, 第三个参数为配置项。
var obj ={};
Object.defineProperty(obj,"a",{
value:12,
writable:true,
enumerable:true,
configurable:true
})
console.log(obj.a); // 12
obj.a = 'abc';
console.log(obj.a); // abc
for(var i in obj)
console.log(i); // a
对象默认是可修改可遍历的,修改对象为不可写,它的属性值便不会被改变,类似于我们用const定义一个常量。 定义为不可遍历,那么它对应的那个属性便无法被遍历,这就可以解释我们开头讲到的为什么无法遍历函数的arguments、length、name等属性名。
var obj ={};
Object.defineProperty(obj,"a",{
value:100,
writable:false,
enumerable:false,
configurable:true
})
console.log(obj.a);// 100
obj.a = "ccc"; // 不会报错,但没法修改值
console.log(obj.a); // 100
for(var key in obj) // 不会被执行,因为不可遍历
console.log(key);
configurable默认是false,属性值不可修改或删除
var obj ={};
var obj = Object.defineProperty({},"a",{
value:"aaa"
})
delete obj.a; // 不会报错,但此属性不会被删除
console.log(obj.a); // aaa
obj.a = 'bbb';
console.log(obj.a); // aaa
重头戏! get和set的读写监听:
var obj ={};
Object.defineProperty(obj,"b",{
set:function(a){
console.log("正在进行赋值"+a);
},
get:function(){
return "aaa"
}
})
obj.b="bbb"; // 正在进行赋值bbb
console.log(obj.b); // aaa
兴许你会觉得上边的这段代码很简单,但是这个get和set的api其实异常强大。时下比较流程的mvvm的框架都是基于Object.defineProperty这个api进行封装的,尤其是set方法的监听使用使得前端产生了数据驱动的思想。但是由于Object.defineProperty这个api在IE8中有bug,所以只能用于IE9+,所以相应的市面流行的React 、vue等框架也只能兼容到IE9+ 。
Object.defineProperty这个api虽然强大,但同样存在着一些问题。除了我们上边提到的浏览器兼容性,它在各浏览器的实现也略有差异。另外当Object.prototype被污染时,使用Object.defineProperty很容易程序崩溃,这种情况在各大浏览器均会出现。因此在计划使用这个api封装自己的框架之前最好还是更加深入的学习了解一下,收集一些专业的hack补丁。