it-swarm.com.de

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

Aktualisierung am 2018.10.31

Dieser Fehler wurde in iOS 12.1 behoben, wünsche dir einen schönen Tag ~

Ich habe ein Problem mit dem Wertestatus des Arrays in der neu veröffentlichten Version von iOS 12 Safari gefunden, z. B. folgenden Code:

<!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 Demo-Seite. Versuchen Sie es mit iOS 12 Safari: https://abelyao.github.io/others/ios12-safari-bug.html

432
abelyao

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 folgender Funktion:

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

Alle zurückgegebenen Array-Referenzen von Aufrufen von buildArray() werden mit demselben Speicher verknüpft, und bei einigen Methoden wie toString() werden die Ergebnisse zwischengespeichert. Normalerweise werden die Daten aus Gründen der Konsistenz bei veränderlichen Operationen auf solchen optimierten Arrays in einen separaten Speicherbereich kopiert und mit diesem verknüpft. Dieses Muster wird Copy-on-Write oder kurz CoW genannt.

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

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

271
hax

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

Dies ist ein Fehler in Webkit . Dies wurde zwar am Ende behoben, aber noch nicht mit iOS GM 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;
    }
  }
})();
14
jsist

Es scheint nicht zwischengespeichert zu werden, wenn sich die Anzahl der Elemente ändert.
Ich konnte das so 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>
6
Atsushi Sasaki