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脚本
Dec 12 Javascript
Iframe 自动适应页面的高度示例代码
Feb 26 Javascript
探究JavaScript函数式编程的乐趣
Dec 14 Javascript
JS代码实现百度地图 画圆 删除标注
Oct 12 Javascript
概述如何实现一个简单的浏览器端js模块加载器
Dec 07 Javascript
浅谈Angular路由复用策略
Oct 04 Javascript
vscode中vue-cli项目es-lint的配置方法
Jul 30 Javascript
Vue 实现拖动滑块验证功能(只有css+js没有后台验证步骤)
Aug 24 Javascript
elementui之el-tebs浏览器卡死的问题和使用报错未注册问题
Jul 06 Javascript
Webpack按需加载打包chunk命名的方法
Sep 22 Javascript
vue请求数据的三种方式
Mar 04 Javascript
实用的 vue tags 创建缓存导航的过程实现
Dec 03 Vue.js
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
php pcntl_fork和pcntl_fork 的用法
2009/04/13 PHP
php修改时间格式的代码
2011/05/29 PHP
浅谈php提交form表单
2015/07/01 PHP
php、java、android、ios通用的3des方法(推荐)
2016/09/09 PHP
PHP基于phpqrcode类生成二维码的方法详解
2018/03/14 PHP
Google Map Api和GOOGLE Search Api整合实现代码
2009/07/18 Javascript
jQuery之日期选择器的深入解析
2013/06/19 Javascript
不想让浏览器运行javascript脚本的方法
2015/11/20 Javascript
JavaScript根据CSS的Media Queries来判断浏览设备的方法
2016/05/10 Javascript
js方法数据验证的简单实例
2016/09/17 Javascript
jquery中用函数来设置css样式
2016/12/22 Javascript
ES6教程之for循环和Map,Set用法分析
2017/04/10 Javascript
详解vue组件化开发-vuex状态管理库
2017/04/10 Javascript
Vue之Watcher源码解析(2)
2017/07/19 Javascript
vue2.0 兄弟组件(平级)通讯的实现代码
2018/01/15 Javascript
vue代理和跨域问题的解决
2018/07/18 Javascript
小程序兼容安卓和IOS数据处理问题及坑
2018/09/18 Javascript
JS中循环遍历数组的四种方式总结
2021/01/23 Javascript
[02:28]DOTA2亚洲邀请赛附加赛 RECAP赛事回顾
2015/01/29 DOTA
python使用PIL模块实现给图片打水印的方法
2015/05/22 Python
python构建深度神经网络(DNN)
2018/03/10 Python
python Celery定时任务的示例
2018/03/13 Python
pyspark 读取csv文件创建DataFrame的两种方法
2018/06/07 Python
使用python将excel数据导入数据库过程详解
2019/08/27 Python
python zip()函数的使用示例
2020/09/23 Python
在pycharm中使用pipenv创建虚拟环境和安装django的详细教程
2020/11/30 Python
材料采购员岗位职责
2013/12/17 职场文书
电厂厂长岗位职责
2014/01/02 职场文书
写自荐信三大法宝
2014/01/24 职场文书
环保建议书
2014/03/12 职场文书
婚纱摄影师求职信范文
2014/04/17 职场文书
《沉香救母》教学反思
2014/04/19 职场文书
演讲稿的格式及范文
2014/08/22 职场文书
护士自荐信范文
2015/03/25 职场文书
小学英语教师研修感悟
2015/11/18 职场文书
CKAD认证中部署k8s并配置Calico插件
2022/03/31 Servers