2018.10.31更新 此错误已在iOS 12.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>

刷新页面后,数组的值仍然相反。这是新Safari的bug还是新功能?


这是一个演示页面。尝试在iOS 12 Safari中使用它: https://abelyao.github.io/others/ios12-safari-bug.html


我写了一个程序库来修复这个bug。 https://www.npmjs.com/package/array-reverse-polyfill

这是代码:

(函数(){ 函数buggy() { Var a = [1,2]; 返回String(a) === String(a.reverse()); } 如果(!车())返回; var r = Array.prototype.reverse; Array.prototype.reverse =函数reverse() { if (Array.isArray(this)) this。长度= this.length; 返回r.call(这个); } }) ();


这绝对是一个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中的一个bug。虽然这个问题已经解决了,但还没有发布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;
    }
  }
})();