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

版权声明:本文为微信公众号前端小二版权所有,如需转载可私信或关注文末微信公众号留言。 https://blog.csdn.net/qq_34832846/article/details/86488043

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

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

检测属性特性:

var obj = {
    a: 2
}
console.log(Object.getOwnPropertyDescriptor(obj, 'a'));
//打印
/**
configurable: true
enumerable: true
value: 2
writable: true
/

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

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

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

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

var newObj = {};
Object.defineProperty(newObj, 'a', {
    value: 10,
    writable: true,
    enumerable: true,
    configurable: true
})
console.log(newObj.a);      // 10

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

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

var obj = {};
Object.defineProperty(obj, 'a', {
    writable: false,
    configurable: false
})

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

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

var obj = {
    //给a定义一个getter
    get a(){
        return 3;
    }
}

Object.defineProperty(obj, 'b', {
    //给b设置一个getter
    get: function(){return this.a*2;},
    //确保b会出现对象的属性列表中
    enmuerable: true
})

console.log( obj.a );   // 3
console.log( obj.b );   // 6

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

扫描二维码关注公众号,回复: 5044984 查看本文章

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

var obj = {
    get a(){
        return 2;
    }
}

obj.a = 10;
console.log(obj.a);     // 2

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

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

var obj = {
    get a(){
        return this.res;
    },
    set a(val){
        this.res = val;
    }
}
obj.a = 10;
console.log( obj.a );   // 10

obj.a = 5;
console.log( obj.a );   // 5

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

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

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

var obj = {a:2};
console.log(obj.hasOwnProperty('a'));   // true

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

var emptyObj = Object.create(null);
emptyObj.a = 11;
console.log('hasOwnProperty' in emptyObj);      // true emptyObj对象无法访问hasOwnProperty方法

console.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()得到的属性列表,这里只包含可枚举属性。

喜欢本文请扫下方二维码,关注微信公众号: 前端小二,查看更多我写的文章哦,多谢支持。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_34832846/article/details/86488043
今日推荐