JavaScript的Object.defineProperty详解


Posted in Javascript onJuly 09, 2018

=与Object.defineProperty

为JavaScript对象新增或者修改属性,有两种不同方式:直接使用=赋值或者使用Object.defineProperty()定义。如下:

// 示例1
var obj = {};

// 直接使用=赋值
obj.a = 1;

// 使用Object.defineProperty定义
Object.defineProperty(obj, "b",
{
 value: 2
});

console.log(obj) // 打印"{a: 1, b: 2}"

这样看两者似乎没有区别,对吧?但是,如果使用Object.getOwnPropertyDescriptor()查看obj.a与obj.b的属性的描述描述符(property descriptor)时,会发现=与Object.defineProperty并不一样:

// 示例2
var obj = {};

obj.a = 1;

Object.defineProperty(obj, "b",
{
 value: 2
});

console.log(Object.getOwnPropertyDescriptor(obj, "a")); // 打印"{value: 1, writable: true, enumerable: true, configurable: true}"
console.log(Object.getOwnPropertyDescriptor(obj, "b")); // 打印"{value: 2, writable: false, enumerable: false, configurable: false}"

可知,使用=赋值时,属性的属性描述符value是可以修改的,而writable、enumerable和configurable都为true。

而使用Object.defineProperty()定义的属性的属性描述符writable、enumerable和configurable默认值为false,但是都可以修改。对于writable、enumerable和configurable的含义,从名字就不难猜中,后文也会详细介绍。

使用=赋值,等价于使用Object.defineProperty()定义时,同时将writable、enumerable和configurable设为true。代码示例3和4是等价的:

// 示例3
var obj = {};

obj.name = "Fundebug";
console.log(Object.getOwnPropertyDescriptor(obj, "name")); // 打印{value: "Fundebug", writable: true, enumerable: true, configurable: true}
// 示例4
var obj = {};

Object.defineProperty(obj, "name",
{
 value: "Fundebug",
 writable: true,
 enumerable: true,
 configurable: true
});
console.log(Object.getOwnPropertyDescriptor(obj, "name")); // 打印{value: "Fundebug", writable: true, enumerable: true, configurable: true}

Object.defineProperty()

使用Object.defineProperty()定义时若只定义value,则writable、enumerable和configurable默认值为false。代码示例5和6是等价的:

// 示例5
var obj = {};

Object.defineProperty(obj, "name",
{
 value: "Fundebug"
});
console.log(Object.getOwnPropertyDescriptor(obj, "name")); // 打印{value: "Fundebug", writable: false, enumerable: false, configurable: false}
// 示例6
var obj = {};

Object.defineProperty(obj, "name",
{
 value: "Fundebug",
 writable: false,
 enumerable: false,
 configurable: false
});
console.log(Object.getOwnPropertyDescriptor(obj, "name")); // 打印{value: "Fundebug", writable: false, enumerable: false, configurable: false}

由于writable、enumerable和configurable都是false,导致obj.name属性不能赋值、不能遍历而且不能删除:

// 示例7
var obj = {};

Object.defineProperty(obj, "name",
{
 value: "Fundebug"
});

// writable为false,无法赋值
obj.name = "云麒";
console.log(obj.name); // 打印"Fundebug"

// enumerable为false,无法遍历
console.log(Object.keys(obj)); // 打印"[]"

// configurable为false,无法删除
delete obj.name;
console.log(obj.name); // 打印"Fundebug"

若在严格模式(“use strict”)下,示例7中的代码会报错,下文可见。

writable

writable为false时,属性不能再次赋值,严格模式下会报错“Cannot assign to read only property”

// 示例8
"use strict"

var obj = {};

Object.defineProperty(obj, "name",
{
 value: "Fundebug",
 writable: false,
 enumerable: true,
 configurable: true
});

obj.name = "云麒"; // 报错“Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'”

writable为true时,属性可以赋值,这一点读者不妨自行测试。

enumerable

enumerable为false时,属性不能遍历:

// 示例9
"use strict"

var obj = {};

Object.defineProperty(obj, "name",
{
 value: "Fundebug",
 writable: true,
 enumerable: false,
 configurable: true
});

console.log(Object.keys(obj)) // 打印"[]"

enumerable为true时,属性可以遍历,这一点读者不妨自行测试。

configurable

enumerable为false时,属性不能删除,严格模式下会报错“Cannot delete property”:

// 示例10
"use strict"

var obj = {};

Object.defineProperty(obj, "name",
{
  value: "Fundebug",
  writable: true,
  enumerable: true,
  configurable: false
});

delete obj.name // 报错“Uncaught TypeError: Cannot delete property 'name' of #<Object>”

enumerable为true时,属性可以删除,这一点读者不妨自行测试。

writable与configurable

当writable与enumerable同时为false时,属性不能重新使用Object.defineProperty()定义,严格模式下会报错“Cannot redefine property”:

// 示例11
"use strict"

var obj = {};

Object.defineProperty(obj, "name",
{
  value: "Fundebug",
  writable: false,
  configurable: false
})

Object.defineProperty(obj, "name",
{
  value: "云麒"
}) // 报错“Uncaught TypeError: Cannot redefine property: name”

当writable或者enumerable为true时,属性可以重新使用Object.defineProperty()定义,这一点读者不妨自行测试。

本文所有代码示例都在Chrome 67上测试。

参考

Object.defineProperty()

Object.getOwnPropertyDescriptor()

StackOverflow: Why can't I redefine a property in a Javascript object?

Javascript 相关文章推荐
浅析js封装和作用域
Jul 09 Javascript
jquery模拟SELECT下拉框取值效果
Oct 23 Javascript
用js格式化金额可设置保留的小数位数
May 09 Javascript
Jquery中$.post和$.ajax的用法小结
Apr 28 Javascript
jQuery弹层插件jquery.fancybox.js用法实例
Jan 22 Javascript
深入理解jquery跨域请求方法
May 18 Javascript
利用jquery禁止外层滚动条的滚动
Jan 05 Javascript
关于Vue实现组件信息的缓存问题
Aug 23 Javascript
Vue2仿淘宝实现省市区三级联动
Apr 15 Javascript
js中值引用和地址引用实例分析
Jun 21 Javascript
js实现简单的秒表
Jan 16 Javascript
Vue使用JSEncrypt实现rsa加密及挂载方法
Feb 07 Javascript
Vue2.0仿饿了么webapp单页面应用详细步骤
Jul 08 #Javascript
mac上配置Android环境变量的方法
Jul 08 #Javascript
vue.js使用watch监听路由变化的方法
Jul 08 #Javascript
vue.js通过路由实现经典的三栏布局实例代码
Jul 08 #Javascript
jQuery插件实现弹性运动完整示例
Jul 07 #jQuery
vue.js使用v-pre与v-html输出HTML操作示例
Jul 07 #Javascript
vue.js实现格式化时间并每秒更新显示功能示例
Jul 07 #Javascript
You might like
第十节--抽象方法和抽象类
2006/11/16 PHP
PHP curl使用实例
2015/07/02 PHP
解读PHP的Yii框架中请求与响应的处理流程
2016/03/17 PHP
PHP读取并输出XML文件数据的简单实现方法
2017/12/22 PHP
PHP单例模式实例分析【防继承,防克隆操作】
2019/05/22 PHP
通用JS事件写法实现代码
2009/01/07 Javascript
javascript下判断一个元素是否存在的代码
2010/03/05 Javascript
JavaScript arguments 多参传值函数
2010/10/24 Javascript
Jquery公告滚动+AJAX后台得到数据
2011/04/14 Javascript
js 去掉空格实例 Trim() LTrim() RTrim()
2014/01/07 Javascript
使用Jquery实现每日签到功能
2015/04/03 Javascript
jQuery+HTML5实现手机摇一摇换衣特效
2015/06/05 Javascript
JavaScript合并两个数组并去除重复项的方法
2015/06/13 Javascript
纯javascript实现的小游戏《Flappy Pig》实例
2015/07/27 Javascript
Angular表单验证实例详解
2016/10/20 Javascript
JS控制div跳转到指定的位置的几种解决方案总结
2016/11/05 Javascript
JavaScript中捕获与冒泡详解及实例
2017/02/03 Javascript
基于datepicker定义自己的angular时间组件的示例
2018/03/14 Javascript
vue最简单的前后端交互示例详解
2018/10/11 Javascript
[01:02]DOTA2上海特锦赛SHOWOPEN
2016/03/25 DOTA
Python中的startswith和endswith函数使用实例
2014/08/25 Python
Python找出文件中使用率最高的汉字实例详解
2015/06/03 Python
python os.listdir按文件存取时间顺序列出目录的实例
2018/10/21 Python
python读写csv文件方法详细总结
2019/07/05 Python
在Django admin中编辑ManyToManyField的实现方法
2019/08/09 Python
15行Python代码实现免费发送手机短信推送消息功能
2020/02/27 Python
基于Python实现2种反转链表方法代码实例
2020/07/06 Python
中国领先的专业家电网购平台:国美在线
2016/12/25 全球购物
运行时异常与一般异常有何异同?
2014/01/05 面试题
如何在Oracle中查看各个表、表空间占用空间的大小
2015/10/31 面试题
某某同志考察材料
2014/05/28 职场文书
房屋买卖委托书格式范本格式
2014/10/13 职场文书
公司行政主管岗位职责
2015/04/09 职场文书
应收账款管理制度
2015/08/06 职场文书
Go语言特点及基本数据类型使用详解
2022/03/21 Golang
Oracle查看表空间使用率以及爆满解决方案详解
2022/07/23 Oracle