在js中for_in主要是用来遍历对象的可枚举属性,包括原型链上的属性。然而for_in在IE < 9下可能会出现问题。
for_in要出现问题必须满足两个条件:
1:IE < 9;
2:某些不可枚举的属性被重写。
在js中当一些不可枚举的属性比如toString被重写后,它会变成可枚举。然而当你碰上了IE<9,这些被重写的属性依旧
是不可枚举无法使用for_in去遍历。那么如何在IE<9的情况下去搜集这些被重写的不可枚举属性。
思路:
一、先重写一个不可枚举属性并用propertyIsEnumerable去检测是否可枚举来判断是否是IE < 9;
二、将不可枚举的属性组成数组以待后续遍历;
三、先拿到对象的原型,constructor属性要单独检测,至于为什么,个人感觉是因为并不是所有对象都有constructor,只有通过构造函数得到的对象才有该属性,检测constructor属性,在排除原型链的情况下判断对象上是否具有constructor属性并且得到的属性数组中
不包含constructor。其他属性,主要是从不可枚举的属性数组中遍历,判断属性数组中是否包含此属性并且该属性属
于该对象并且原型上的该属性不等于该对象上此属性。
underscore.js中给出以下方法。
一、首先定义hasEnumBug来判断是否是IE<9
var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');//返回false
二、然后列出IE<9下不能用for in来枚举的key值集合
var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
三、最后搜集方法
var collectNonEnumProps = function(obj, keys) {
var nonEnumIdx = nonEnumerableProps.length;
var constructor = obj.constructor;
//获得原型,两种情况,构造函数和字面量形式 obj.constructor.prototype 和 obj.__proto__
var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;
// `constructor` 属性需要特殊处理
var prop = 'constructor';
//对象重写了constructor 并且keys数组不包含constructor
if (_.has(obj, prop) && !_.contains(keys, prop)) {
keys.push(prop);
}
//遍历nonEnumerableProps数组
while (nonEnumIdx--) {
prop = nonEnumerableProps[nonEnumIdx];
//判断是否重写了不可枚举属性
//in用来判断一个属性是不是属于一个对象,这个属性可以是原型链上的,也可以是不可枚举属性
if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
keys.push(prop);
}
}
};