IOS 8 Safari JIT bug影响jQuery和underscore

前端时间为移动游戏做一个网页活动需求(9宫格的刮奖),遇到一个很诡异的问题:Android端OK,就是在Ios设备上,点击非第一块区域,显示却是第一块区域被刮开咯,查看后端返回数据缺失OK的【前端采用backbone写法,遍历返回数据用的是underscore.js中的each】。经过一番查证,原来如此。

参考水木社区这里的叙述,知晓原来这个属于:IOS 8 Safari JIT bug 影响 jQuery 和 underscore,致使: $.each/.each,看到 .length 后,会把一个 object 当作数组来遍历。这个bug 会造成莫名其妙的结果。而我那里的结果就是:object数据对象,会在.each遍历之后被乱序了。然后就造成以上所描述的问题。

于此,当时的解决办法是:将后端返回的object对象数据,做了转数组处理后,set给定义的模板,交付给_.each遍历就没有问题了。

var arrList = [];
for(var i=0; i<9; i++){
truearrList.push(data.datainit.list[i]);
};
this.model.set('arrList',arrList);

好吧,可以看到这里for循环用的是这种传统的方式(还有一个魔数9,额の)。一开始,这里用for..in方式进行遍历的,代码如下:

for (k in data.datainit.list){
  arrList.push(data.datainit.list[k]);
}

惊奇的结果是:这样搞,不行的;好奇怪的样子(得更努力的深入Js才行哇~)?在chrome的console下比较两者转化的数组结果一模一样。然后以ipod机器(系统版本Ios8.4.1)alert数组的长度,得到的结果是一样的,都是9。额,奇怪啊。就在即将放弃的时候alert了下转化数组结果,发现采用for..in 方式遍历object对象数据,在ios机器上(没试过所有,拿了ipad,iphone相对高版本系统)会打乱原有的顺序(原来以为是以value从大到小呢,后来多次尝试,不是如此,也未发现规律);可见,Ios8 Safari上,以for..in方式遍历object对象会造成乱序;但是为何会造成这样子呢?按照Jquery方面对此问题的修复时说法,此时该Object对象the highest property is 10,且是从data.datainit.list[1]开始轮询的~?。

JQuery方面在 https://github.com/jquery/jquery/issues/2145 有过对此问题的叙述:

There is a timing bug in iOS8 that causes mobile Safari to incorrectly report a ‘length’ on objects that don’t have one. To the best of my knowledge, this happens on iOS8+, possibly only on 64-bit systems. The bug is triggered for objects that have only numeric properties. For example:

foo = { 1: 'a', 2: 'b', 3: 'c' }

In this case, if you query foo.length then mobile Safari will sometimes return 4 (the highest property + 1). This causes functions like $.each() to treat objects such as foo above as arrays instead of objects, and when it tries to iterate them as such it fails since there is no foo[0]. The problem can be fixed in the function isArrayLike(). Instead of just checking for

typeof length === "number"

you also need to check for obj.hasOwnProperty(‘length’) The latter check is immune to the iOS bug. I realize this is a fix just for one browser, but it’s a browser with a very large user base.


当然:jQuery 1.11.3 和 underscore 1.8.3修正了这个问题。事实上,对于此问题:jquery方面在Github这里$.each fails intermittently on iOS due to Safari bug#2145有过相关的论述;有兴趣一探究竟,可以点进去看下。

来对比下underscore 1.7.0和underscore1.8.3中_.each的代码差异,来窥测下其修正手法:

// The cornerstone, an `each` implementation, aka `forEach`. Handles raw objects in addition to array-likes. Treats all sparse array-likes as if they were dense.   _.each = _.forEach = function(obj, iteratee, context) {
  if (obj == null) return obj;
  iteratee = createCallback(iteratee, context);
  var i, length = obj.length;
  if (length === +length) {
    for (i = 0; i < length; i++) {
      iteratee(obj[i], i, obj);
    }
  } else {
    var keys = _.keys(obj);
    for (i = 0, length = keys.length; i < length; i++) {
      iteratee(obj[keys[i]], keys[i], obj);
    }
  }
  return obj;   };

在underscore1.8.3中,做了如下改进,并附有相应注释:

// Helper for collection methods to determine whether a collection
// should be iterated as an array or as an object
// Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
// Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = property('length');
var isArrayLike = function(collection) {
  var length = getLength(collection);
  return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};

var property = function(key){
  return function(obj){
    return obj == null ? void 0 : obj[key];
  }
}

// Collection Functions
// --------------------

// The cornerstone, an `each` implementation, aka `forEach`.
// Handles raw objects in addition to array-likes. Treats all
// sparse array-likes as if they were dense.
_.each = _.forEach = function(obj, iteratee, context) {
  iteratee = optimizeCb(iteratee, context);
  var i, length;
  if (isArrayLike(obj)) {
    for (i = 0, length = obj.length; i < length; i++) {
      iteratee(obj[i], i, obj);
    }
  } else {
    var keys = _.keys(obj);
    for (i = 0, length = keys.length; i < length; i++) {
      iteratee(obj[keys[i]], keys[i], obj);
    }
  }
  return obj;
};

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android机器圈

Java设计模式总汇一

PS:首先我们要带着问题读文章 什么是设计模式 为什么要用设计模式 使用设计模式有什么好处   设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设...

377110
来自专栏Android知识点总结

1--安卓网络编程之获取IP地址

41020
来自专栏大内老A

WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[上篇]

对于上一篇文章 (WCF基本异常处理模式:[上篇]、[中篇]、[下篇]),主要是站在最终开发者的角度对WCF关于异常处理编程模式进行了介绍,接下来,我们需要将我...

20890
来自专栏王清培的专栏

Redis 数据结构与内存管理策略(上)

Redis 数据结构与内存管理策略(上) 标签: Redis Redis数据结构 Redis内存管理策略 Redis数据类型 Redis类型映射 作者:王清培(...

34170
来自专栏Pythonista

Python之路,Day1 - Python基础1

python的创始人为吉多·范罗苏姆(Guido van Rossum)。1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解...

20550
来自专栏醒者呆

精雕细琢——全方位解析单例模式

单例模式有的时候特别重要,因为某些系统是要求某个类在整个生命周期中有且只有一个实例存在,这时候就要用到单例模式。 保证一个类仅有一个实例,并提供一个访问它的全...

34740
来自专栏编程

设计模式启示录(二)

设计模式启示录(二) 在【设计模式启示录 (一)】中,重点介绍了设计模式的精髓(抽象),设计模式的分类(按抽象的目的进行分类)。在本篇中,将按照前述的七大分类,...

18470
来自专栏数说戏聊

07.时间处理&抽取1.时间处理1.1 字符型转时间型2.时间抽取

6210
来自专栏北京马哥教育

shell十三问,为linux学习打基础(二)

本文整理并转自CU上的帖子[学习共享] shell 十三問?,此贴是2003年发表的,但却是相当不错的linux基础知识汇集贴,原帖主使用的台湾风格,本文加以简...

40140
来自专栏Python自动化测试

selenium框架浅谈

我们知道,selenium是一个很优秀的web框架,提供了很丰富的API,使用它结合进行做web的自动化测试真的很完美,但是在实际的情况中,理想与现实...

12930

扫码关注云+社区

领取腾讯云代金券