ECMAScript 5中的属性描述符详解


Posted in Javascript onMarch 02, 2015

属性描述符是ES5中新增的概念,其作用是给对象的属性增加更多的控制。

Object.defineProperty

要研究属性描述符,首先要谈谈 Object.defineProperty 方法。这个方法的作用是给对象定义新属性或修改已存在的属性。其原型如下:

Object.defineProperty(obj, prop, descriptor)

使用示例:
var obj = { };

Object.defineProperty(obj, 'attr', { value: 1 });

上面一段代码给obj对象增加了一个名为attr的属性,值为1。相当于:
var obj = { };

obj.attr = 1;

相比起来,Object.defineProperty 的写法看似更为复杂。但是,它最大的奥秘在于其第三个参数。

数据描述符

假设我们希望attr是一个只读属性,就可以加上 writable 数据描述符:

var obj = { };

Object.defineProperty(obj, 'attr', {

    value: 1,

    writable: false

});

console.log(obj.attr);

obj.attr = 2; // fail

console.log(obj.attr);

执行以上程序可以发现,两次打印出来的attr的值都是1,也就是说对属性的写入失败。然而,这样的结果会有点莫名其妙,因为赋值语句的执行没有异常,却失败了,试想如果在大片的代码中出现这样的问题,就很难排查出来。事实上,只要以严格模式运行代码,就会产生异常:
'use strict'; // 进入严格模式

var obj = { };

Object.defineProperty(obj, 'attr', {

    value: 1,

    writable: false

});

obj.attr = 2;  // throw exception

下面再来看看另一个数据描述符 enumerable ,它可以控制属性是否能被枚举。如果只是简单地定义一个属性,这个属性是可以在for...in循环中被枚举出来的:
var obj = { };

obj.attr = 1;

for (var i in obj) { console.log(obj[i]); }

enumerable 可以将其“藏”起来:
var obj = { };

Object.defineProperty(obj, 'attr', {

    value: 1,

    enumerable: false

});

for (var i in obj) { console.log(obj[i]); }

执行上面一段代码,会发现控制台什么也没输出,因为此时attr属性无法被枚举了。

讲到这里,大家可能有一个疑问,属性描述符能否被修改?比方说一个只读属性是否可以再次定义为可写?其实这取决于另一个数据描述符 configurable ,它可以控制属性描述符能否被更改。

var obj = { };

Object.defineProperty(obj, 'attr', {

    value: 1,

    writable: false,

    configurable: true

});

Object.defineProperty(obj, 'attr', {

    writable: true

});

obj.attr = 2;

上面一段代码先把attr定义为只读属性,然后又重新定义为可写。所以对attr的写入是成功的。

存取描述符

存取描述符类似面向对象中的get/set访问器。

var obj = { };

Object.defineProperty(obj, 'attr', {

    set: function(val) { this._attr = Math.max(0, val); },

    get: function() { return this._attr; }

});

obj.attr = -1;

console.log(obj.attr); // 0

在上面一段代码中,对attr的访问事实上变成了对_attr的访问,而且在set函数中限制了最小值为0。

获取属性描述符

前面所述都是设置属性描述符,那如何获取已设置的描述符呢?Object.getOwnPropertyDescriptor 可以完成此项工作。

var obj = { };

Object.defineProperty(obj, 'attr', {

    value: 1,

    writable: false,

    configurable: true

});

var desc = Object.getOwnPropertyDescriptor(obj, 'attr');

console.dir(desc);

对象控制

前面说的 Object.defineProperty ,其操作的是对象的属性,而下面说的三个方法则直接操作对象。

Object.preventExtensions 可以使对象无法拥有新的属性:

var obj = { };

obj.attr = 1;

Object.preventExtensions(obj);

obj.attr2 = 2; //fail

Object.seal 可以使对象仅剩属性值可以修改(如果属性为只读,则连属性值都无法修改):
var obj = { };

obj.attr = 1;

Object.seal(obj);

obj.attr = 1.5;

delete obj.attr; // fail

Object.freeze 可以使对象完全无法被修改:
var obj = { };

obj.attr = 1;

Object.freeze(obj);

obj.attr = 1.5; // fail

obj.attr2 = 2; //fail

然后大家可能又会问,怎么知道某个对象是否曾经被preventExtensions、seal或者freeze呢?答案就是分别调用 Object.isExtensible 、 Object.isSealed 、 Object.isFrozen ,这三个函数的用法比较简单,就不再累赘了。

总的来说,通过属性描述符可以进一步严格控制对象,加强程序逻辑的严谨性,唯一不足的就是,ES5在IE9里面才基本实现(IE9还不支持严格模式),考虑到国内IE8份额还比较高的情况,这套东西目前只能在移动端浏览器和Node.js里面用了。

Javascript 相关文章推荐
脚本之家贴图转换+转贴工具用到的js代码超级推荐
Apr 05 Javascript
JavaScript 和 Java 的区别浅析
Jul 31 Javascript
判断iframe里的页面是否加载完成
Jun 06 Javascript
Javascript模块化编程详解
Dec 01 Javascript
JavaScript中的console.group()函数详细介绍
Dec 29 Javascript
JS延时器提示框的应用实例代码解析
Apr 27 Javascript
几句话带你理解JS中的this、闭包、原型链
Sep 26 Javascript
jQuery实现checkbox列表的全选、反选功能
Nov 24 Javascript
vue.js实现格式化时间并每秒更新显示功能示例
Jul 07 Javascript
用 js 写一个 js 解释器过程详解
Aug 02 Javascript
使用vue-router切换页面时实现设置过渡动画
Oct 31 Javascript
浅谈vue中resetFields()使用注意事项
Aug 12 Javascript
JS+CSS实现可以凹陷显示选中单元格的方法
Mar 02 #Javascript
JavaScript数组常用方法
Mar 02 #Javascript
使用npm发布Node.JS程序包教程
Mar 02 #Javascript
js实现点击链接后窗口缩小并居中的方法
Mar 02 #Javascript
运行Node.js的IIS扩展iisnode安装配置笔记
Mar 02 #Javascript
Javascript动画的实现原理浅析
Mar 02 #Javascript
JavaScript页面模板库handlebars的简单用法
Mar 02 #Javascript
You might like
我的论坛源代码(六)
2006/10/09 PHP
php 数组使用详解 推荐
2011/06/02 PHP
thinkphp自定义权限管理之名称判断方法
2017/04/01 PHP
PHP 实现重载
2021/03/09 PHP
Extjs在exlipse中设置自动提示的方法
2010/04/07 Javascript
Javascript 去除数组的重复元素
2010/05/04 Javascript
javascript高级学习笔记整理
2011/08/14 Javascript
2016年最热门的15 款代码语法高亮工具,美化你的代码
2016/01/06 Javascript
JavaScript预解析及相关技巧分析
2016/04/21 Javascript
Bootstrap3使用typeahead插件实现自动补全功能
2016/07/07 Javascript
js实现选项卡内容切换以及折叠和展开效果【推荐】
2017/01/08 Javascript
简单谈谈gulp-changed插件
2017/02/21 Javascript
JSON与js对象序列化实例详解
2017/03/16 Javascript
JS通过调用微信API实现微信支付功能的方法示例
2017/06/29 Javascript
JQuery元素快速查找与操作
2018/04/22 jQuery
Vuex 单状态库与多模块状态库详解
2018/12/11 Javascript
12 种使用Vue 的最佳做法
2020/03/30 Javascript
linecache模块加载和缓存文件内容详解
2018/01/11 Python
PyQt QListWidget修改列表项item的行高方法
2019/06/20 Python
Python Pandas数据结构简单介绍
2019/07/03 Python
Python实现CNN的多通道输入实例
2020/01/17 Python
Python开发之pip安装及使用方法详解
2020/02/21 Python
python+selenium 脚本实现每天自动登记的思路详解
2020/03/11 Python
django项目中新增app的2种实现方法
2020/04/01 Python
用Python在Excel里画出蒙娜丽莎的方法示例
2020/04/28 Python
HTML5 直播疯狂点赞动画实现代码 附源码
2020/04/14 HTML / CSS
几个常见的软件测试问题
2016/09/07 面试题
国培计划培训感言
2014/03/11 职场文书
商务经理岗位职责
2014/07/30 职场文书
委托书怎样写
2014/08/30 职场文书
怎样写家长意见
2015/06/04 职场文书
推广普通话主题班会
2015/08/17 职场文书
2016党员学习作风建设心得体会
2016/01/21 职场文书
导游词之珠海轮廓
2019/10/25 职场文书
Android中View.post和Handler.post的关系
2022/06/05 Java/Android
Java多线程并发FutureTask使用详解
2022/06/28 Java/Android