-
Notifications
You must be signed in to change notification settings - Fork 642
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
for ... in 存在的浏览器兼容问题你造吗 #3
Comments
I don't understand why you use two different conditions to check if an object's property is being overwritten. Also in this case, I think |
这个 我跟楼主一样 看的也是一脸懵逼 |
|
楼主,你感觉下面的这种情况有意义吗?但是这种情况用的场景还是挺多的,就是显示声明一个对象的constructor。这个时候,严谨的说是更改constructor的指向 var obj = {constructor:Object}
//这时候
obj.constructor === Object.prototype.constructor //true |
what '造' mean in Chinese ? |
@image72 means "知道" |
关于prop in obj,应该是为了避免一种特殊情况,就是obj的原型链是null的情况。(如果不要这个判断的话,会是以下结果) var o = {}
var keys = []
collectNonEnumProps(o, keys)
// keys = []
o.__proto__ = null
// or Object.setPrototypeOf(o, null)
collectNonEnumProps(o, keys)
// keys = ["toLocaleString", "hasOwnProperty", "propertyIsEnumerable", "toString", "isPrototypeOf", "valueOf"] |
_.has(obj, prop) 与 (prop in obj) 有什么区别呢? |
@tsanie 你说的是对的。 如果把“prop in obj”注掉的话,keys = ["hasOwnProperty", "propertyIsEnumerable", "toString", "isPrototypeOf", "valueOf"], 这里发现少了toLocalString,是因为Object.prototype中也没有这个属性。 不过这里用Object.create(null)举例可能不太合适,毕竟这是ES5的方法,但collectNonEnumProps方法时专门为ES3准备的,可能 |
hasOwnProperty 会获取 obj对象 本身的 可迭代和不可迭代的属性, 不会获取原型上面的任何属性; 可以去看 mdn 详解。 |
@2json 请问那么为什么不能全部用hasOwnProperty的方法判断呢?也就是不单独处理constructor,而是将所有属性都像constructor一样处理。实际上,我根本就不明白为什么单单要把constructor单独处理…… |
你好,请问下_.keys和_.allKeys方法的主要区别在哪里?虽然从代码和代码注释上可以看到,.allKeys遍历所有的属性名,而.keys只遍历自有属性(包括被重写的继承来的属性),但是我没有找到两个方法返回结果不同的例子。所有尝试的结果两者返回的都是一样的,可以帮忙举个返回结果不同的例子吗?谢谢 |
其实我还是不理解为什么不全部用_.has来判断,还有确实像上面所说可能会出现这种情况:
但是其他方法也一样会出现这种情况,比如:
我认为这一样是没法用prop in obj && obj[prop] !== proto[prop]这句来判断的,因为一定会返回false。如果用_.has来判断反而靠谱的多。 |
发现另外一个bug: |
@tsanie 你说的这个我测试出来有些不同,下面是我的测试结果 var o = {}
var keys = []
collectNonEnumProps(o, keys)
// keys = ['toString']
o.__proto__ = null
collectNonEnumProps(o, keys)
// keys = [] 另外我看的 function collectNonEnumProps(obj, keys) {
var nonEnumIdx = nonEnumerableProps.length;
var proto = typeof obj.constructor === 'function' ? FuncProto : ObjProto;
while (nonEnumIdx--) {
var prop = nonEnumerableProps[nonEnumIdx];
// if判断我觉得可以改成 if (_.has(obj, prop) && !_.contains(keys, prop)) { }
if (prop === 'constructor' ? _.has(obj, prop) : prop in obj &&
obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
keys.push(prop);
}
}
} |
Why underscore
最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中。
阅读一些著名框架类库的源码,就好像和一个个大师对话,你会学到很多。为什么是 underscore?最主要的原因是 underscore 简短精悍(约 1.5k 行),封装了 100 多个有用的方法,耦合度低,非常适合逐个方法阅读,适合楼主这样的 JavaScript 初学者。从中,你不仅可以学到用 void 0 代替 undefined 避免 undefined 被重写等一些小技巧 ,也可以学到变量类型判断、函数节流&函数去抖等常用的方法,还可以学到很多浏览器兼容的 hack,更可以学到作者的整体设计思路以及 API 设计的原理(向后兼容)。
之后楼主会写一系列的文章跟大家分享在源码阅读中学习到的知识。
欢迎围观~ (如果有兴趣,欢迎 star & watch~)您的关注是楼主继续写作的动力
for ... in
今天要跟大家聊聊 for ... in 在浏览器中的兼容问题。
for ... in 大家应该都不陌生,循环只遍历可枚举属性。像 Array 和 Object 使用内置构造函数所创建的对象都会继承自 Object.prototype 和 String.prototype 的不可枚举属性,例如 String 的 indexOf() 方法或者 Object 的 toString 方法。循环将迭代对象的所有可枚举属性和从它的构造函数的 prototype 继承而来的(包括被覆盖的内建属性)。
我们举个简单的例子:
等等,你跟我说 for ... in 这玩意有浏览器兼容性?!从来没注意过啊,好像工作中也没碰到过这样的兼容性问题啊!确实如此,for ... in 要出问题,得满足两个条件,其一是在 IE < 9 浏览器中(又是万恶的 IE!!),其二是被枚举的对象重写了某些键,比如 toString。
还是举个简单的例子:
ok,在 chrome 中我们 alert 出了预期的 "toString",而在 IE 8 中啥都没有弹出。
我们回头看看 for ... in 的作用,循环遍历 可枚举属性,那么显然 IE 8 将 toString "内定" 成了不可枚举属性(尽管已经被重写)。那么如何判断是否在类似 IE 8 这样的环境中呢?underscore 中有个 hasEnumBug 函数就是用来做这个判断的:
代码一目了然,用了 propertyIsEnumerable 方法。
那么哪些属性被重写之后不能用 for ... in 在 IE < 9 下枚举到呢?有如下这些:
恩,应该还漏了个 constructor。
我们来看看 underscore 是怎么做的。
proto 变量保存了原型,一个对象的原型可以通过 obj.constructor.prototype 获取,但是如果重写了 constructor 很显然就无法这样获取了,则用 Object.prototype 替换。这样比如说重写了 toString,我们只需要比较 obj.toString 是否和 proto.toString 引用相同即可。个人觉得源码中的 prop in obj 判断多余了,这不肯定返回 true 吗?如果有理解错误,望指出。
而对于重写了 constructor 的情况,underscore 用 hasOwnProperty 进行判断。
对于重写了以上几种属性的情况,underscore 确实能够获取其在 IE < 9 中的键,但是爱钻牛角尖的楼主也十分不解,constructor 真的有必要和其他属性分开来检测吗?
对于 toString 这样的属性被重写,underscore 的判断非常好,如果没有被重写,那么对象的 toString 方法肯定是继承于原型链的,判断对象的 toString 方法是否和原型链上的一致即可,但是用 hasOwnProperty 能判断吗?楼主觉得也是可以的,hasOwnProperty 方法用来判断对象的 key 是否是自有属性,即是否来自于原型链,如果被重写了,那么应该会返回 true,否则 false。
而被重写的 constructor 能否用 obj[prop] !== proto[prop] 来判断呢?楼主觉得也是可以的,如果没有被重写,那么 obj.constructor === obj.constructor.prototype.constructor 返回 true,如果被重写,obj.constructor === Object.prototype.constructor 返回 false。
关于这点,楼主也是百思不得其解,但是很显然 constructor 属性和其他属性是有明显区别的,从代码理解角度来看,也是 underscore 这样处理比较容易接受。如果是楼主理解有出入的地方,还望指出!
最后,小结下,对于 for ... in 在 IE < 9 下的兼容问题,楼主感觉并没有那么重要,毕竟谁会没事去重写这些属性呢!所以,知道有这么一回事就可以了。
最后的最后,给出这部分源码位置,有兴趣的同学可以看下 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/src/underscore-1.8.3.js#L904-L946
The text was updated successfully, but these errors were encountered: