2018.10.31 更新 iOS 12.1 修复了这个 bug,祝你有美好的一天~
我在新发布的 iOS 12 Safari 中发现 Array 的值状态有问题,例如,代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>iOS 12 Safari bugs</title>
<script type="text/javascript">
window.addEventListener("load", function ()
{
let arr = [1, 2, 3, 4, 5];
alert(arr.join());
document.querySelector("button").addEventListener("click", function ()
{
arr.reverse();
});
});
</script>
</head>
<body>
<button>Array.reverse()</button>
<p style="color:red;">test: click button and refresh page, code:</p>
</body>
</html>
刷新页面后,数组的值仍然是反转的。这是新 Safari 的错误还是功能?
这是一个演示页面。尝试将其与 iOS 12 Safari 一起使用:https://abelyao.github.io/others/ios12-safari-bug.html
绝对是BUG!这是一个非常严重的错误。
该错误是由于数组初始值设定项的优化,其中所有值都是原始文字。例如,给定函数:
function buildArray() {
return [1, null, 'x'];
}
从对 buildArray()
的调用返回的所有数组引用都将链接到同一内存,并且某些方法(例如 toString()
)将缓存其结果。通常,为了保持一致性,对此类优化数组的任何可变操作都会将数据复制到单独的内存空间并链接到它;这种模式称为 copy-on-write,简称 CoW。
reverse()
方法会改变数组,因此它应该触发写入时复制。但事实并非如此,因为最初的实现者(Apple 的 Keith Miller)错过了 reverse()
案例,尽管他已经编写了许多测试用例。
此错误是 8 月 21 日的 reported to Apple。修复程序 landed in the WebKit repository 是 8 月 27 日,并于 2018 年 10 月 30 日在 Safari 12.0.1 和 iOS 12.1 中发布。
我写了一个库来修复这个错误。 https://www.npmjs.com/package/array-reverse-polyfill
(function() { function buggy() { var a = [1, 2]; return String(a) === String(a.reverse()); } if(!buggy()) return; var r = Array .prototype.reverse; Array.prototype.reverse = function reverse() { if (Array.isArray(this)) this.length = this.length; return r.call(this); } })();
this.length = this.length
)会触发写时复制,所以会改变数组的内存地址,所以会修复reverse
的行为。
这是 webkit 中的一个错误。虽然这已经在他们的最后解决了,但尚未随 iOS GM 版本一起发布。此问题的解决方案之一:
(function() {
function getReverseStr() {
return [1, 2].reverse();
}
var n1 = getReverseStr()[0];
var n2 = getReverseStr()[0];
// check if there is an issue
if(n1 != n2) {
var origReverseFunction = Array.prototype.reverse;
Array.prototype.reverse = function() {
var newArr = this.slice();
// use original reverse function so that edge cases are taken care of
origReverseFunction.apply(newArr, arguments);
var that = this;
// copy reversed array
newArr.forEach(function(value, index) {
that[index] = value;
});
return this;
}
}
})();
如果元素数量发生变化,它似乎不会被缓存。我能够避免这种情况。
测试:点击按钮刷新页面,代码:
正文>