前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《你不知道的JavaScript》:js对象的属性特性和枚举深入

《你不知道的JavaScript》:js对象的属性特性和枚举深入

作者头像
前端_AWhile
发布2019-08-29 13:12:19
1K0
发布2019-08-29 13:12:19
举报
文章被收录于专栏:前端一会前端一会

《你不知道的JavaScript》第二部分 对象 第 2 篇。

自ES5开始,js中的对象属性具有属性描述符。可以直接检测与定义属性特性。

检测属性特性:

代码语言:javascript
复制
 1var obj = {
 2    a: 2
 3}
 4console.log(Object.getOwnPropertyDescriptor(obj, 'a'));
 5//打印
 6/**
 7configurable: true
 8enumerable: true
 9value: 2
10writable: true
11/
12

可以看到,检测属性的结果打印为4个属性数据描述符:value(属性值)、writable(可写)、enumerable(可枚举)、configurable(可配置)。

  • value是属性的值。
  • 后三者的默认值均为true
  • writable特性就是控制属性是否可改写;
  • enumerable特性是控制属性是否会出现在对象的属性枚举中,所谓的可枚举,就相当于 “可以出现在对象属性的遍历中”,比如for…in循环;
  • configurable特性就是控制属性是否可配置,即是否能通过defineProperty()方法来修改属性特性,当该特性值为false时,属性就不可配置。

在用对象字符量方式创建对象时,对象的属性特性均会使用默认值。

如果想自定义属性特性,可以通过Object.defineProperty()来添加一个新属性或者修改一个已有属性,当然想自定义的前提是configurable属性要为true。

代码语言:javascript
复制
1var newObj = {};
2Object.defineProperty(newObj, 'a', {
3    value: 10,
4    writable: true,
5    enumerable: true,
6    configurable: true
7})
8console.log(newObj.a);      // 10

上例就是通过Object.defineProperty()来为对象newObj添加一个属性a,并为这个属性配置了相关的属性特性。当然这种只是示例,在实际开发中不推荐这样定义一个对象,除非是要修改属性特性。

通过Object.defineProperty()来控制对象属性的特性,比较好玩的一个实现就是生成一个真正的常量属性(不可修改、重定义或者删除):

代码语言:javascript
复制
1var obj = {};
2Object.defineProperty(obj, 'a', {
3    writable: false,
4    configurable: false
5})

当然还有其他好玩的实现,请多研究吧。

ES5对象属性除了有四个数据描述符,还有两个访问描述符getter和setter。当对属性定义访问描述符时,js会忽略它们的 valuewritable特性,而改为关心 setget以及configurableenumerable特性。

代码语言:javascript
复制
 1var obj = {
 2    //给a定义一个getter
 3    get a(){
 4        return 3;
 5    }
 6}
 7
 8Object.defineProperty(obj, 'b', {
 9    //给b设置一个getter
10    get: function(){return this.a*2;},
11    //确保b会出现对象的属性列表中
12    enmuerable: true
13})
14
15console.log( obj.a );   // 3
16console.log( obj.b );   // 6

不管是在对象字面量中的 get a(){...}还是在defineProperty()中的显式定义,二者都会在对象中创建一个不包含值的属性。

对于这个值的访问会自动调用一个隐藏函数,它的返回值会被当作属性访问的返回值:

代码语言:javascript
复制
1var obj = {
2    get a(){
3        return 2;
4    }
5}
6
7obj.a = 10;
8console.log(obj.a);     // 2

你看,即使再次对属性a进行set操作,返回值依然是是get隐藏函数的返回值,从而让set操作没有意义,也再次验证使用访问描述符时,js会忽略它们的value和writable特性。

所以为了让属性更合理,可以获取也可以修改值,还应当定义setter。通常getter和setter是成对出现的:

代码语言:javascript
复制
 1var obj = {
 2    get a(){
 3        return this.res;
 4    },
 5    set a(val){
 6        this.res = val;
 7    }
 8}
 9obj.a = 10;
10console.log( obj.a );   // 10
11
12obj.a = 5;
13console.log( obj.a );   // 5

最后再来看下对象中属性的存在性检测:

  • in操作符会检查属性是否在对象及其原型链中
  • hasOwnProperty()只会检查属性是否在对象中,不会检查到原型链中

所有普通对象都可以通过对Object.protptype的委托来访问hasOwnProperty()方法

代码语言:javascript
复制
1var obj = {a:2};
2console.log(obj.hasOwnProperty('a'));   // true

但有的对象可能是由于没有连接到Object.prototype而不能访问hasOwnProperty()方法,此时可以通过 call/apply 来借用:

代码语言:javascript
复制
1var emptyObj = Object.create(null);
2emptyObj.a = 11;
3console.log('hasOwnProperty' in emptyObj);      // true emptyObj对象无法访问hasOwnProperty方法
4
5console.log(Object.prototype.hasOwnProperty.call(emptyObj, 'a'));   // true 

前几篇this的绑定规则还记得不,四个绑定规则里有一个是显式绑定,上例就是通过显式绑定来把Object.prototype.hasOwnProperty方法里的this绑定到emptyObj对象上,以达到借用hasOwnProperty方法的目的。

补充个对象的枚举知识,有几点需要注意:

  • in操作符可以用来判断属性是否在对象及其原型链中,
  • for…in…操作符只可以用来判断属性是否可枚举,即属性特性enumerable为true时可枚举
  • propertyIsEnumerable()会检查给定的属性名是否直接存在于对象中(而不是存在于原型链中),并且还需满足enumerable: true
  • Object.keys()会返回一个数组,包含所有可枚举属性
  • Object.getOwnPropertyNames()会返回一个数组,包含所有属性,无论它们是否可枚举
  • inhasOwnProperty()的区别在于是否查找原型链,然而Object.keys()Object.getOwnPropertyNames()都只会查找对象直接包含的属性
  • 目前并没有内置的方法可以获取in操作符使用的属性列表(对象本身的属性及原型链上的属性)。不过可以递归遍历某个对象的整条原型链并保存每层中使用Object.keys()得到的属性列表,这里只包含可枚举属性。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端小二 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档