数组状态将缓存在 iOS12Safari 中。这是一个 bug 还是一个特性?

更新于2018.10.31

这个 bug 已经在 iOS12.1中修复了,祝你今天愉快 ~

我发现最新发布的 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>

刷新页面之后,数组的值仍然是相反的。这是一个 bug 还是新 Safari 的一个特性?


这是一个演示页面,试着和 iOS12 Safari 一起使用: Https://abelyao.github.io/others/ios12-safari-bug.html

80141 次浏览
我写了一个库来修复这个错误。 https://www.npmjs.com/package/array-reverse-polyfill < / p >

这就是代码:

(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);
}
})();

这绝对是一个BUG!这是一个非常严重的漏洞。

该错误是由于数组初始化器的优化,其中所有值都是原始文字。例如,给定函数:

function buildArray() {
return [1, null, 'x'];
}

buildArray()调用返回的所有数组引用都将链接到相同的内存,并且一些方法(如toString())将缓存它们的结果。通常,为了保持一致性,对这种优化数组的任何可变操作都会将数据复制到单独的内存空间并链接到它;这个模式被称为即写即拷,或简称为CoW。

reverse()方法会改变数组,因此它应该触发写时复制。但事实并非如此,因为最初的实现者(Apple的Keith Miller)遗漏了reverse()用例,尽管他已经编写了许多测试用例。

此错误在8月21日向苹果公司报告。8月27日修复了登陆WebKit存储库,并于2018年10月30日在Safari 12.0.1和iOS 12.1中发布。

如果元素数量改变,它似乎不会被缓存

<!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];
arr.push('');
arr.pop();
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>

这是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;
}
}
})();