【问题标题】:Array state will be cached in iOS 12 Safari. Is it a bug or feature?数组状态将缓存在 iOS 12 Safari 中。它是错误还是功能?
【发布时间】:2019-02-22 17:08:05
【问题描述】:

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 的错误还是功能?


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

【问题讨论】:

  • 在 macOS 10.14 Mojave 中也确认了错误 - i.imgur.com/ZJtJJC1.png
  • macOS 10.13.6 (High Sierra) 和 Safari 版本 12.0 (13606.2.11) 有同样的问题。刷新页面后数组仍然是反转的。
  • 该漏洞已在 Safari 12.0.1 (macOS) 和 iOS 12.1 中得到修复。

标签: javascript ios safari ios12


【解决方案1】:

这绝对是一个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 中发布。

【讨论】:

  • 注意:Mac OS X 上的 Safari 12.0 也有同样的问题。
  • 是的,它已经在源代码中修复,并且已经在 Safari Technology Preview 中发布。在 Safari Technology Preview 65 中尝试cdn.miss.cat/demo/ios12-safari-bug.html。你会发现它没有这个 bug。
  • 我不认为该错误的根本原因是索引混淆的结果;相反,这似乎是由于在修改对象之前忽略检查对象是否不可变造成的。切片问题可能有类似的解释,但它不一样,但据我所知,不会被补丁修复为反向。您应该考虑为切片问题打开 WebKit 错误报告。
  • @Zenexer 你是对的。我在找到bugs.webkit.org/show_bug.cgi?id=188794 并查看源代码之前写了这个答案。我将编辑我的答案。
【解决方案2】:

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

This is the code:

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

【讨论】:

  • 随时更新。欢迎投稿。
  • @zephi,我猜想写长度(this.length = this.length)会触发写时复制,所以会改变数组的内存地址,所以会修复reverse的行为。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    如果元素数量发生变化,似乎没有被缓存。
    我能够避免这种情况。

    <!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>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-08
      • 2013-05-08
      • 2015-01-20
      • 1970-01-01
      • 2022-07-27
      • 1970-01-01
      相关资源
      最近更新 更多