从枚举属性谈JS对象的遍历
最近查资料的时候发现了一些有趣的事情,主要是从Object.create这个函数中发现了一些知识漏洞,于是想梳理一下。
创建对象的简单方法大概有以下几种
function Person() {
this.x = 5;
}
let a1 = {x: 5};
let a2 = new Person();
let a3 = Object.create({x: 5});
let a4 = Object.create({}, {
x: 5
});
以上四种方法创建的对象均可以使用a.x访问到该对象的x属性,但其中还是有一些差别的。
-
a1是通过对象字面量创建的实例 与a1 = new Object({x: 5})创建出来的实例没有差别。
-
a2是通过构造函数创建的对象 其constructor属性指向的Person构造函数,proto属性指向的是Person构造函数的prototype。
-
a3是通过Object.create函数创建的实例,以第一个参数{x: 5}为原型对象创建的,故a3.x访问的是原型链上的x属性。
-
a4是通过Object.create函数创建的实例,以第一个参数{}为原型对象,第二个参数为属性配置创建的。
以上示例的对象不仅具有原理上的不同,在遍历中也有不同的表现。对对象的遍历我们可以想到使用for in 循环。for in循环的原理则是遍历对象及其原型链上所有的可枚举属性。
function iterator(x) {
for(let i in x) {
console.log(i);
}
}
iterator(a1) // 输出x
iterator(a2) // 输出x
iterator(a3) // 输出x
iterator(a4) // 输出undefined
我们发现了a4方式创建的对象与其它3种方法在使用for in方法遍历时的不同,可以看到他的属性描述符都被置为false。
Object.getOwnPropertyDescriptor(a2,"x") // 输出 Object {value: 5, writable: false, enumerable: false, configurable: false}
-
查阅了JavaScript权威指南后发现,如果指定可选的descriptors属性,如Object.create(p,d)等同于Object.defineProperties(Object.create(p),d)
-
所以如果不指定描述符为true的情况下所有描述符都会默认指定为false。了解这种创建对象属性的方法很有利于设置我们遍历中不想获得的属性。
-
另外for in 会枚举到所有原型链上的可枚举属性,解决的办法有2个
// 第一种解决办法
function iterator(x) {
for(let i in x) {
if (x.hasOwnProperty(i))
console.log(i);
}
}// 第二种解决办法 Object.keys(x) // 获取所有非继承的可枚举属性再for遍历