js遍历Object对象可枚举属性、不可枚举属性、原型链属性和自身属性
利用hasOwnProperty、getOwnPropertyNames、Object.keys、for…in遍历对象的可枚举属性、不可枚举属性、原型链上的属性和自身属性。
首先来看看各种方法的用法及兼容性。
hasOwnProperty
Object.prototype.hasOwnProperty(name) 方法用来判断某个对象是否含有指定的自身属性,它本身是对象原型链上的默认方法。
hasOwnProperty 方法是 ES3 就标准化了,所以不用担心兼容性问题。
但由于 hasOwnProperty 是原型链上的方法,所以有可能被对象的自身方法所屏蔽。如:
var foo = {
hasOwnProperty: function() {
return false;
},
bar: 'Here be dragons'
};
foo.hasOwnProperty('bar'); // 始终返回 false
// 如果担心这种情况,可以直接使用原型链上真正的 hasOwnProperty 方法
({}).hasOwnProperty.call(foo, 'bar'); // true
Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
getOwnPropertyNames
Object.getOwnPropertyNames(obj)方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性)组成的数组, 该方法不会获取到原型链上的属性。如:
// 父类构造函数
function ParentClass() {}
ParentClass.prototype.inheritedMethod = function() {};
// 继承
var ChildClass = Object.create(ParentClass.prototype, {
// 不可枚举属性
getFoo: {
value: function() { return this.foo; },
enumerable: false
}
});
// 自身可枚举属性
ChildClass.foo = 1;
// 再定义一个原型链属性
ChildClass.prototype.prototypeMethod = function() {};
console.log(Object.getOwnPropertyNames(my_obj).sort()); // ["foo", "getFoo"]
兼容性到 IE9, es 5标准。
Object.keys()
返回该对象的所有可枚举自身属性的属性名。
//具有随机键排序的数组类对象
var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(an_obj)); // console: ['2', '7', '100']
这些属性的顺序与手动遍历(如for..in)该对象属性时的一致。
注意:Object.keys 和 getOwnPropertyNames ,在ES5,如果此方法的参数不是一个对象(原始的),那么它会造成 TypeError。在ES6,非对象的参数将被强制转换为一个对象。
Object.keys("foo");
Object.getOwnPropertyNames("foo");
// TypeError: "foo" is not an object (ES5 code)
Object.keys("foo");
// ["0", "1", "2"] (ES6 code)
Object.getOwnPropertyNames("foo");
// ["0", "1", "2", "length"] (ES6 code)
兼容性到 IE9, es 5标准。
for..in
手动遍历,可以遍历到自身和原型链上所有可枚举的属性。
只获取自身可枚举属性
- 直接用
Object.keys()
- IE9 以下可以通过
hasOwnProperty
实现:
if (!Object.keys) Object.keys = function(o) {
if (o !== Object(o))
throw new TypeError('Object.keys called on a non-object');
var k=[],p;
for (p in o) if (Object.prototype.hasOwnProperty.call(o,p)) k.push(p);
return k;
}
只获取自身不可枚举属性
- 通过
Object.keys()
排除可枚举属性
var target = myObject;
var enum_and_nonenum = Object.getOwnPropertyNames(target);
// 排除可枚举属性
var enum_only = Object.keys(target);
var nonenum_only = enum_and_nonenum.filter(function(key) {
var indexInEnum = enum_only.indexOf(key);
if (indexInEnum == -1) {
// not found in enum_only keys mean the key is non-enumerable,
// so return true so we keep this in the filter
return true;
} else {
return false;
}
});
console.log(nonenum_only);
- 通过
propertyIsEnumerable
判断,该方法返回Boolean值,不可枚举返回false。
var target = myObject;
var enum_and_nonenum = Object.getOwnPropertyNames(target);
var enum_only = Object.keys(target);
var nonenum_only = enum_and_nonenum.filter(function(key) {
// 筛选不可枚举属性
return !target.propertyIsEnumerable(key)
});
console.log(nonenum_only);
只获取原型链上的可枚举属性
- 直接用 for..in
var o = Object.getPrototypeOf(targetObj); // 跳过遍历自身属性,直接从原型上开始
var k=[],p;
for (p in o) if (Object.prototype.hasOwnProperty.call(o,p)) k.push(p);
return k;
注意,这样遍历出来的属性也不一定能访问, 因为自身属性可能会屏蔽掉原型上的属性。
只获取原型链上的不可枚举属性
比较麻烦,如果使用 getOwnPropertyNames,只能单独获取一层原型链,必须一级一级遍历往上找,而且这也不一定有场景,因为原型链上的方法重名是会被屏蔽的。