前言:for in 这个遍历方法相信小伙伴们肯定很熟悉,在日常开发中经常用到。今天来探讨一下在开发中笔者曾遇到疑问。
for...in 语句以任意顺序迭代一个对象的除 Symbol 以外的可枚举属性,包括继承的可枚举属性。
我们直接开始继承的枚举示例:
let obj = {
a: 'a',
b: 'b',
}
//在原型上绑定一个属性,然后打印
Object.prototype.c = 'c'
for (let i in obj) {
console.log(i)
}
打印结果毋庸置疑是:a,b,c。我们都知道,for in 会遍历对象的属性(包括原型上的属性和手动添加的属性)。那问题来了,在Object.prototype上有很多属性的,例如toString valueOf等。
那为什么这些属性没有被遍历出来呢?这时候我们再认真读一下最开始对for in的描述。会发现可枚举属性这个词。
可枚举可以理解为是否可以被遍历 被列举出来,可枚举性决定了这个属性能否被for…in查找遍历到
那么我们要怎么知道这个属性是否为可枚举的呢?这时候需要用到Object.getOwnPropertyDescriptors(需要查找的目标对象,目标对象内属性名称)。
Object.getOwnPropertyDescriptor()方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)。- 如果指定的属性存在于对象上,则返回其属性描述符对象,否则返回 undefined。
属性描述符:
value:该属性的值。writable:当且仅当属性的值可以被改变时为 true。get:获取该属性的访问器函数(getter)。如果没有访问器,该值为 undefined。set:获取该属性的设置器函数(setter)。如果没有设置器,该值为 undefined。configurable:当且仅当指定对象的属性描述可以被改变或者属性可被删除时,为 true。enumerable:当且仅当指定对象的属性可以被枚举出时,为 true。
let obj = {
a: 'a',
b: 'b',
}
Object.prototype.c = 'c'
let a = Object.getOwnPropertyDescriptor(Object.prototype, 'c')
let toString = Object.getOwnPropertyDescriptor(Object.prototype,"toString")
console.log(a)
console.log(toString)
我们可以看到enumerable属性两者不同,toString方法是不可枚举,所以在for in作用下没有被遍历出来。
既然知道了是因为enumerable来控制是否可枚举。那么我们可以通过obj.propertyIsEnumerable(需要测试的属性名)来判断这个属性是否可枚举。
let aEn = obj.propertyIsEnumerable('a')
//因为前面是挂载Object的原型上,如果在定义的obj上查找是查找不到。
let cEn = Object.prototype.propertyIsEnumerable('c')
let toStringEn=Object.prototype.propertyIsEnumerable('toString')
console.log(aEn)//true
console.log(cEn)//true
console.log(toStringEn)//false
在日常开发中,我们较为常用Object.hasOwnProperty(属性名)来来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
let obj = {
a: 'a',
b: 'b',
}
Object.prototype.c = 'c'
for (let i in obj) {
//打印出a,b
if (obj.hasOwnProperty(i)) {
console.log(i)
}
}
如果这个属性是在该对象原型自己定义的,但是别人又不想每次遍历这个对象都要用hasOwnProperty判断是否为公有属性怎么办。我们结合上面的属性符描述就可以实现。利用object.defineproperty()来实现。
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。他可以定义该属性的各个属性,例如set get enumerable等。
Object.defineProperty(obj, prop, desc):
obj第一个参数就是要在哪个对象身上添加或者修改属性prop第二个参数就是添加或修改的属性名desc配置项,一般是一个对象
let obj = {
a: 'a',
b: 'b',
}
Object.defineProperty(Object.prototype, 'c', {
value: function () {
console.log('c')
},
writable: true,
enumerable: false,
configurable:true
})
for (let i in obj) {
console.log(i)
}
//打印出a,b
经过在Object.defineProperty()定义里面将enumerable设置为false,最终这个属性在for in打印出来的结果会被忽略掉。
总结:这篇文章分享就到此为止。在开发中其实一开始只是好奇for in为啥不能遍历自身的方法,以此出发,慢慢了解到了属性操作符,认识Object对象的几个原生方法,也了解Object.defineProperty()操作方法。学习就要保持一份好奇心,这样知识才会慢慢积累。最后感谢大家的阅读(* ̄︶ ̄)。