阵列状态将在iOS 12 Safari中被缓存。这是一个错误还是特性?

在2018.10.31更新

这个bug已经在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的特点?


这里有一个演示页面。试着用iOS 12 Safari来使用它。 https://abelyao.github.io/others/ios12-safari-bug.html

对该问题的评论 (3)

这绝对是一个BUG!而且是一个非常严重的错误。

这个bug是由于数组初始化器的优化造成的,其中所有的值都是原始字元。例如,给定一个函数。

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

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

reverse()方法突变了数组,所以它应该触发写时复制。但是它没有,因为最初的实现者(苹果公司的Keith Miller)错过了reverse()的情况,尽管他写了很多测试案例。

这个错误在8月21日被报告给苹果。8月27日,该修复方案登陆WebKit仓库,并于2018年10月30日在Safari 12.0.1和iOS 12.1中出货。

评论(4)

我写了一个lib来修复这个错误。 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);
  }
})();
评论(2)

这是[webkit][1]的一个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;
    }
  }
})();

[1]: https://bugs.webkit.org/show_bug.cgi?id=188794

评论(0)

如果元素的数量发生变化,似乎就不会被缓存了。




    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
    iOS 12 Safari bugs
    <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>


    Array.reverse()
    <p style="color:red;">test: click button and refresh page, code:</p>

评论(0)