iOS 12 的 safari 有哪些变化吗?今天发现了一个 javascript 的问题

2018-09-18 21:09:38 +08:00
 34C

正在开发的程序中,有一段代码大概如下:

window.addEventListener("load", function ()
{
    var arr = [1, 2, 3, 4];
    arr.reverse();
});

今天升级了 iOS 12 之后,无意中发现数组的顺序有问题。

第一次加载是正常的, 页面刷新会颠倒, 再刷新又正常了, 再刷新又颠倒了, 以此类推…

而如果是关闭了页面、重新打开,则不会有问题。

在 PC Chrome / Android / iOS 11 Safari 中都没出现这个问题,就好像这个变量,被 safari “缓存” 了一样,即使刷新页面也会被保留、继承之前的状态。

这是 iOS 12 Safari 的 bug 还是 feature 啊?有没有哪里可以看到关于 safari 底层机制的改变呢?

9715 次点击
所在节点    问与答
31 条回复
uuair
2018-09-18 21:18:35 +08:00
adblock 没法用了。。
34C
2018-09-18 21:20:48 +08:00
@uuair 求帮顶,如果这个 “ feature ” 真的存在,那依赖 js 变量做状态的(比如 var userinfo 之类的),要出很多大坑了
34C
2018-09-18 21:27:05 +08:00
经测试,改成 var arr = new Array(1, 2, 3, 4); 之后则没问题,稍后 append 到主题中。
看样子如果不是明确 new 的话是会被缓存了……
gkiwi
2018-09-18 21:46:58 +08:00
@34C 找人测试了,果然如此,这个世界要爆炸。。
34C
2018-09-18 21:56:39 +08:00
@gkiwi 想象一下有多少人升级 12 而不会升级 12.1 的,如果是 bug 以后即便修复了也会有人一直受影响,如果是 feature 这特么也太严重了,多少项目多少代码会受影响,还有 npm 上那些被数千万项目引用的包…
gkiwi
2018-09-18 22:06:37 +08:00
@34C #5 定性肯定是 bug。我这边测试不加 load,直接 alert 出来数据都偶发不对。
一方面浏览器修,二方面就是靠 pollify 了
34C
2018-09-18 22:10:13 +08:00
@gkiwi 我这边测试是 load 事件都会触发,但 load 中申明的变量,有概率会被缓存,太坑了,还好项目开发了一半,有改语法的机会
yinanc
2018-09-18 22:22:41 +08:00
mark
前端日常骂娘时间
34C
2018-09-18 22:25:30 +08:00
@yinanc 顶上去让更多人看到,V2 上前端还蛮多的
yinanc
2018-09-18 22:32:55 +08:00
@34C 真是这么严重的问题的话很快就会有轩然大波了。不如自己先去确认下吧。
34C
2018-09-18 22:46:04 +08:00
@yinanc 所以我的标题是问有哪些变化啊… 因为我这是小项目,也没提前升级测试版 iOS 12,所以来问问看苹果之前有没有公开说明过 safari 会有那些改动啊,除了苹果官方的说明是一种 “确认” 我不知道还能怎么自己先确认了,我和楼上的都测试过可以复现问题,难道是我对 js 的工作机制理解有误… 那 ios 11 和 chrome 之类的都没问题啊…
edire
2018-09-19 02:53:40 +08:00
写了个文件修复这个问题:

https://www.npmjs.com/package/fix-array-reverse

https://github.com/fanmingfei/array-reverse-ios12

目前发现只有使用 reverse 才会缓存,另外,不知道苹果是否会热更新修复这个问题。
edire
2018-09-19 04:44:27 +08:00
npm 现在限制实在太多了。本来想 npm unpublish 一下再 publish,告诉我 24 小时之内不能 publish 索性改了个名字 https://www.npmjs.com/package/array-reverse-polyfill
Trim21
2018-09-19 05:30:35 +08:00
成功复现…
hax
2018-09-19 06:26:41 +08:00
这显然是一个 bug。并且是个惊天大 bug。

此 bug 跟 load 事件无关。直接执行即可重现。与刷新也并不直接相关。
简化的测试代码见: https://github.com/hax/hax.github.com/blob/master/browser-bugs/ios12-safari-array-reverse/test.html
测试页面链接: https://johnhax.net/browser-bugs/ios12-safari-array-reverse/test


此 bug 的本质是,Safari 对所有值是 primitive literal (如 null, true, 1, 'x' 是,但 /x/,undefined、NaN 就不是)的 array initializer 做了优化,同一个 initializer 产生的数组在内存里永远指向一份,其 toString 的结果也预先计算好,所以 reverse()之后 toString()结果不变,但实际数组已经变化。正常来说,如果该 array 执行了任何修改操作,则复制到一份独立内存去。这是所谓 copy-on-write 的优化策略。但不幸的,reverse 方法没有触发 CoW。

另一方面,所有不修改 array 的方法应该不触发 CoW。我实测下来,甚至 copyWithin 和 fill 这样的方法,如果 start/end 相同使得实际上并没有修改效果,也不会触发 CoW。但是神奇的是 slice()会触发 CoW。所以我猜有可能某个苹果的临时工把 reverse/slice 的方法索引搞颠倒了。
34C
2018-09-19 09:27:23 +08:00
@edire @hax 原来 SO 上两条回复也是你们啊…
atian25
2018-09-19 11:28:20 +08:00
@edire 不要 unpublish,直接按 semver 规范发一个 minor 或 patch 就好了
34D
2018-09-19 13:41:13 +08:00
没我大。
34C
2018-09-19 14:10:23 +08:00
@34D 你赢了
oh
2018-09-19 16:42:00 +08:00
@hax 你这个分析并不能解释为什么 safari 为了性能要缓存变量啊,页面都 reload 了内存都不清空,为了性能值得这么冒险吗?那说不定还有其它隐藏 bug 呢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/490590

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX