underscore 源码阅读系列 -- for...in 循环的兼容性问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
shawncheung
V2EX    前端开发

underscore 源码阅读系列 -- for...in 循环的兼容性问题

  •  
  •   shawncheung 2017-10-13 11:46:21 +08:00 2076 次点击
    这是一个创建于 3010 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本系列通过阅读 underscore 源码与实战进而体验函数式编程的思想, 而非通过冗长的文字教程, 细读精度 约 1500 行的 underscore 有利于写出耦合度低, 符合函数式编程思想的代码, 并且可以学到 call 与 apply 执行效率的不同进而进行代码性能优化的巧等.

    欢迎大家 star 或者 watch 本系列, 您的关注是作者的最大动力, 让我们一起持续进步. 本系列仓库: https://github.com/zhangxiang958/underscore-analysis

    兼容 for...in 循环

    我们在遍历对象的时候往往会使用 for...in 循环得到 object 的键值对,但是其实 for...in 是有兼容性问题的。 for in 循环是有 bug 的, 不过 bug 的触发条件是有限制的. 条件有两个:

    1. 需要在 IE9 以下的浏览器
    2. 被枚举的对象被重写了一些不可枚举属性. 下面这段代码是用来兼容 for in 中的 bug 的。在 underscore 里面很少会使用 for..in 来遍历对象, 作者通过 _.keys 方法加上 for 循环代替了 for...in , 但是还是无法避免地在 _.keys 中使用了 for...in 循环:
     if (hasEnumBug) collectNonEnumProps(obj, keys); 

    上面这段代码就是在 _.keys 函数内部中的 for...in 循环中的一句代码, 判断是否有兼容问题, 若有则做兼容处理。 下面是兼容的详细代码:

    var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString'); var nOnEnumerableProps= ['valueOf', 'isPrototypeOf', 'toString', 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString']; var collectNOnEnumProps= function(obj, keys) { var nOnEnumIdx= nonEnumerableProps.length; var cOnstructor= obj.constructor; var proto = _.isFunction(constructor) && constructor.prototype || ObjProto; // Constructor is a special case. var prop = 'constructor'; if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop); while (nonEnumIdx--) { prop = nonEnumerableProps[nonEnumIdx]; if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) { keys.push(prop); } } }; 

    这段代码就是用来兼容 bug 的.

    var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString'); 

    第一: hasEnumBug 是用来判断有没有 for in bug 的, 因为重写了 Object 原型中的 toString 属性,对于有兼容问题的浏览器而言,是打印不出对象中 toString 属性的值的, 因为 toString 属性在那些有问题的浏览器中属于不可重写的不可枚举属性,所以根据这个来判断是否有兼容问题。 然后. 核心在于在 collectNonEnumProps 函数, 第一先确定哪些属性名在低版本浏览器是不可枚举的, 分别是 valueOf, isPrototypeOf, toString, propertyIsEnumberable, hasOwnProperty toLocalString 这几个方法, 当然还有 constructor, 那为什么 constructor 属性需要独立抽取出来做特殊判断呢?

    var cOnstructor= obj.constructor; var proto = _.isFunction(constructor) && constructor.prototype || ObjProto; // Constructor is a special case. var prop = 'constructor'; if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop); 

    因为 constructor 属性有其特殊性,因为对于 toString 那些属性而言, 判断是否需要加入到 keys 数组中是根据 obj[prop0] !== proto[prop] 来判断的, 不一样说明重写了,说明这个属性是用户新定义的属性,需要作为一个数据属性名放到 keys 数组中去,但是 constructor 不能做这个判断,不能单纯使用 === 来判断是否被重写,因为对象在继承的时候 constructor 函数通常都不会等于 Object, 所以 constructor 需要被提出来单独判断。 在函数最后,得到不可枚举的属性名列表接下来就好办了, 就是判断对象里面的和原型链里面的同属姓名的值相不相同, 不相同就是重写了, 将这个属性名加到 keys 数组里面, 如果相同就不加. 这就是为什么一进入函数就需要存储 obj.prototype. 之前有人说说循环里面 prop in obj 多余, 这个并不是, 可以试试: 下面是我在 chrome 浏览器做的测试代码

    var obj = Object.create(null); 'toString' in obj; // false 

    这样是 false 的, underscore 为了避免这样的情况发生, 添加了判断. 最后说一下感受, for in 循环到底有没有必要进行兼容, 不兼容的危害大不大? 我认为并不大, 但是作为一个类库, 代码的严谨性是需要的, 但是我们在平时使用 for in 循环的时候有没有必要担惊受怕呢? 其实没有必要的, 我们如果不是特别需要, IE9 以下的兼容情况会越来越少, 而且一般来说我们也不会在对象里面去覆盖像 toString 这样的原型属性.

    如果觉得有收获, 请到 github 给作者一个 star 表示支持吧, 谢谢大家.

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2396 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 43ms UTC 15:39 PVG 23:39 LAX 07:39 JFK 10:39
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86