js遍历Object对象可枚举属性、不可枚举属性、原型链属性和自身属性

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,只能单独获取一层原型链,必须一级一级遍历往上找,而且这也不一定有场景,因为原型链上的方法重名是会被屏蔽的。

0%