Der Array-Status wird in iOS 12 Safari zwischengespeichert. Ist es ein Fehler oder eine Funktion?

432

Update am 31.10.2018

Dieser Fehler wurde in iOS 12.1 behoben. Ich wünsche Ihnen einen schönen Tag ~

Ich habe ein Problem mit dem Wertstatus von Array in der neu veröffentlichten iOS 12 Safari gefunden, z. B. Code wie folgt:

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

Nach dem Aktualisieren der Seite wird der Wert des Arrays immer noch umgekehrt. Ist dies ein Fehler oder eine Funktion der neuen Safari?


Hier ist eine Demoseite. Versuchen Sie es mit iOS 12 Safari zu verwenden: https://abelyao.github.io/others/ios12-safari-bug.html

abelyao
quelle
41
Fehler
a_rahmanshah
43
macOS 10.13.6 (High Sierra) mit Safari Version 12.0 (13606.2.11) hat das gleiche Problem. Das Array wird nach dem Aktualisieren der Seite immer noch umgekehrt.
Kevin Gimbel
2
Der Fehler wurde in Safari 12.0.1 (macOS) sowie in iOS 12.1 behoben.
MrMister

Antworten:

272

Es ist definitiv ein Fehler! Und es ist ein sehr schwerwiegender Fehler.

Der Fehler ist auf die Optimierung von Array-Initialisierern zurückzuführen, bei denen alle Werte primitive Literale sind. Zum Beispiel mit der Funktion:

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

Alle zurückgegebenen Array-Verweise von Aufrufen an buildArray()werden mit demselben Speicher verknüpft, und bei einigen Methoden, z. B. toString()werden die Ergebnisse zwischengespeichert. Um die Konsistenz zu gewährleisten, kopiert normalerweise jede veränderbare Operation auf solchen optimierten Arrays die Daten in einen separaten Speicherbereich und verknüpft sie damit. Dieses Muster wird als Copy-on-Write oder kurz CoW bezeichnet.

Die reverse()Methode mutiert das Array und sollte daher eine Kopie beim Schreiben auslösen. Dies ist jedoch nicht der reverse()Fall , da der ursprüngliche Implementierer (Keith Miller von Apple) den Fall verpasst hat, obwohl er viele Testfälle geschrieben hat.

Dieser Fehler wurde Apple am 21. August gemeldet . Der Fix landete am 27. August im WebKit-Repository und wurde am 30. Oktober 2018 in Safari 12.0.1 und iOS 12.1 ausgeliefert.

hax
quelle
11
Hinweis: Safari 12.0 unter Mac OS X weist ebenfalls das gleiche Problem auf.
Hax
17
Ja, es wurde bereits in den Quellen behoben und bereits in der Safari Technology Preview ausgeliefert. Versuchen Sie es mit cdn.miss.cat/demo/ios12-safari-bug.html in Safari Technology Preview 65. Sie werden feststellen, dass der Fehler nicht vorliegt .
Sideshowbarker
6
Ich glaube nicht, dass die zugrunde liegende Ursache des Fehlers das Ergebnis einer Indexverwechslung ist. Stattdessen scheint es daran zu liegen, dass nicht überprüft wird, ob ein Objekt unveränderlich ist, bevor es geändert wird. Das Slice-Problem mag eine ähnliche Erklärung haben, aber es ist nicht dasselbe, wird aber, soweit ich das beurteilen kann, vom Patch für die Umkehrung nicht behoben. Sie sollten in Betracht ziehen, einen WebKit-Fehlerbericht für das Slice-Problem zu öffnen.
Zenexer
5
@Zenexer Du hast recht. Ich habe diese Antwort geschrieben, bevor ich die bugs.webkit.org/show_bug.cgi?id=188794 gefunden und den Quellcode gesehen habe. Ich werde meine Antwort bearbeiten.
Hax
75

Ich habe eine Bibliothek geschrieben, um den Fehler zu beheben. https://www.npmjs.com/package/array-reverse-polyfill

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

Edire Fan
quelle
4
Update jederzeit. Willkommen zu beitragen.
Edire Fan
14
@zephi, ich denke, dass das Schreiben auf length ( this.length = this.length) Copy On Write auslöst, also die Speicheradresse des Arrays ändert und so das Verhalten von korrigiert reverse.
Cœur
14

Dies ist ein Fehler im Webkit . Dies wurde zwar am Ende gelöst, aber noch nicht mit iOS GM Release ausgeliefert. Eine der Lösungen für dieses Problem:

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

Es scheint nicht zwischengespeichert zu werden, wenn sich die Anzahl der Elemente ändert.
So konnte ich das vermeiden.

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

Atsushi Sasaki
quelle