javascript - Array-tilstand vil bli bufret i iOS 12 Safari. Er det en feil eller en funksjon?


Oppdatering 31.10.2018 :

Denne feilen er blitt fikset i iOS 12.1, ha en fin dag~

Jeg fant et problem med Arrays verdistat i den nylig utgitte iOS 12 Safari, for eksempel kode som dette:

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

Etter å ha oppdatert siden, er fortsatt verdien til arrayet reversert. Er dette en feil eller en funksjon i nye Safari?


Her er en demoeside. Prøv å bruke den med iOS 12 Safari: https://abelyao.github.io/others/ios12-safari-bug.html

Svar #1

Det er definitivt en FEIL! Og det er en veldig alvorlig feil.

Feilen skyldes optimaliseringen av array-initialisatorer der alle verdier er primitive litteraler. For eksempel, gitt funksjonen:

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

Alle returnerte array-referanser fra kall til buildArray() vil koble til samme minne, og noen metoder som toString() vil ha sine resultater bufret. Normalt, for å bevare konsistens, vil enhver muterbar operasjon på slike optimaliserte arrays kopiere dataene til et eget minneområde og koble til det; dette mønsteret kalles copy-on-write, eller CoW for kort.

Metoden reverse() endrer arrayen, så den skal utløse kopier-ved-skriving. Men det gjør den ikke, fordi den opprinnelige implementøren (Keith Miller fra Apple) gikk glipp av reverse()-tilfellet, selv om han hadde skrevet mange testtilfeller.

Denne feilen ble rapportert til Apple 21. august. Fiksen ble lagt inn i WebKit-repositoriet 27. august og ble levert i Safari 12.0.1 og iOS 12.1 den 30. oktober 2018.

Svar #2

Jeg skrev et bibliotek for å fikse feilen. https://www.npmjs.com/package/array-reverse-polyfill

Dette er koden:

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

Svar #3

Dette er en feil i WebKit. Selv om dette har blitt løst på deres side, har det ennå ikke blitt sendt med iOS GM-utgivelsen. En av løsningene på dette problemet:

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

Relaterte artikler