阵列状态将缓存在iOS 12 Safari中。它是错误还是功能?


432

更新于2018.10.31

此错误已在iOS 12.1中修复,祝您愉快!

我在新发布的iOS 12 Safari中发现数组的值状态存在问题,例如,如下代码:

<!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的功能?


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


41
在macOS 10.14 Mojave中也确认了错误-i.imgur.com/ZJtJJC1.png
a_rahmanshah

43
带有Safari版本12.0(13606.2.11)的macOS 10.13.6(High Sierra)具有相同的问题。刷新页面后,阵列仍然反转。
凯文·金贝尔

2
该错误已在Safari 12.0.1(macOS)和iOS 12.1中修复。
MrMister '18

Answers:


272

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

该错误是由于对数组初始化程序进行了优化,其中所有值都是原始文字。例如,给定功能:

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

从调用返回的所有数组引用buildArray()都将链接到相同的内存,并且某些方法(例如)toString()将缓存其结果。通常,为了保持一致性,对这种优化阵列进行的任何可变操作都会将数据复制到单独的存储空间并链接到该存储空间。这种模式称为写时复制,简称CoW。

reverse()方法会更改数组,因此应触发写时复制。但这不是因为原始的实现者(苹果的基思·米勒)错过了这个reverse()案例,尽管他编写了许多测试用例。

此错误已于8月21日报告给Apple。此修复程序于8月27日登陆WebKit存储库,并于2018年10月30日在Safari 12.0.1和iOS 12.1中发布。


11
注意:Mac OS X上的Safari 12.0也有相同的问题。
2016年

17
是的,它已经在来源中得到修复,并已经在Safari Technology Preview中提供。在Safari Technology Preview 65中尝试cdn.miss.cat/demo/ios12-safari-bug.html。您会发现它没有错误。
sideshowbarker

6
我不认为该错误的根本原因是索引混合的结果。相反,它似乎是由于在修改对象之前忽略检查对象是否不变而引起的。切片问题可能具有类似的解释,但不尽相同,但据我所知,不会被反向修补程序修复。您应该考虑针对切片问题打开WebKit错误报告。
Zenexer

5
@Zenexer你是对的。在找到bugs.webkit.org/show_bug.cgi?id=188794并查看源代码之前,我写了这个答案。我将编辑我的答案。
hax

75

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


4
随时更新。欢迎贡献。
Edire Fan

14
@zephi,我想用length(this.length = this.length)写入将触发写入时复制,因此将更改数组的内存地址,从而解决的行为reverse
心教堂

14

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

6

如果元素数量更改,则似乎不被缓存。
我能够避免这种情况。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.