Stan tablicy będzie buforowany w Safari iOS 12. Czy to błąd lub funkcja?

432

Aktualizacja na 2018.10.31

Ten błąd został naprawiony w iOS 12.1, miłego dnia ~

Znalazłem problem ze stanem wartości Array w nowo wydanym Safari na iOS 12, na przykład taki kod:

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

Po odświeżeniu strony wartość tablicy jest nadal odwracana. Czy to błąd lub funkcja nowego Safari?


Oto strona demonstracyjna. Spróbuj użyć go z iOS 12 Safari: https://abelyao.github.io/others/ios12-safari-bug.html

abelyao
źródło
41
Błąd potwierdzony także w macOS 10.14 Mojave - i.imgur.com/ZJtJJC1.png
a_rahmanshah
43
MacOS 10.13.6 (High Sierra) z Safari w wersji 12.0 (13606.2.11) ma ten sam problem. Tablica jest nadal odwracana po odświeżeniu strony.
Kevin Gimbel,
2
Błąd został naprawiony w Safari 12.0.1 (macOS), a także w iOS 12.1.
MrMister

Odpowiedzi:

272

To zdecydowanie BŁĄD! I to jest bardzo poważny błąd.

Błąd wynika z optymalizacji inicjalizatorów tablicowych, w których wszystkie wartości są pierwotnymi literałami. Na przykład, biorąc pod uwagę funkcję:

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

Wszystkie zwrócone odwołania tablic z wywołań do buildArray()linkują do tej samej pamięci, a niektóre metody, takie jak, toString()będą buforowane swoje wyniki. Zwykle, aby zachować spójność, każda zmienna operacja na takich zoptymalizowanych tablicach spowoduje skopiowanie danych do osobnej przestrzeni pamięci i połączenie z nią; ten wzorzec nazywa się kopiowaniem przy zapisie lub w skrócie CoW.

Te reverse()metody mutuje tablicy, więc powinno wywołać kopiowanie przy zapisie. Ale tak nie jest, ponieważ oryginalny implementator (Keith Miller z Apple) przegapił reverse()przypadek, mimo że napisał wiele przypadków testowych.

Ten błąd został zgłoszony firmie Apple 21 sierpnia. Poprawka trafiła do repozytorium WebKit 27 sierpnia i została dostarczona w Safari 12.0.1 i iOS 12.1 30 października 2018 r.

hax
źródło
11
Uwaga: Safari 12.0 na Mac OS X również ma ten sam problem.
hax
17
Tak, zostało to już naprawione w źródłach i jest już dostarczane w przeglądarce Safari Technology Preview. Spróbuj cdn.miss.cat/demo/ios12-safari-bug.html w Safari Technology Preview 65. Przekonasz się, że nie ma w nim błędu.
sideshowbarker
6
Nie sądzę, że podstawowa przyczyna błędu jest wynikiem pomieszania indeksu; zamiast tego wydaje się, że jest to spowodowane zaniedbaniem sprawdzenia, czy obiekt jest niezmienny przed modyfikacją. Problem z wycinkiem może mieć podobne wytłumaczenie, ale nie jest taki sam, ale o ile wiem, nie będzie naprawiony przez łatkę do tyłu. Powinieneś rozważyć otwarcie raportu o błędzie WebKit dla problemu z wycięciem.
Zenexer,
5
@Zenexer Masz rację. Napisałem tę odpowiedź, zanim znalazłem bugs.webkit.org/show_bug.cgi?id=188794 i zobaczyłem kod źródłowy. Zmienię swoją odpowiedź.
hax
75

Napisałem lib, aby naprawić błąd. https://www.npmjs.com/package/array-reverse-polyfill

To jest kod :

(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
źródło
4
Zaktualizuj w dowolnym momencie. Zapraszamy do współpracy.
Edire Fan,
14
@zephi, myślę, że pisanie na length ( this.length = this.length) uruchomi Copy On Write, więc zmieni adres pamięci tablicy, a więc poprawi zachowanie reverse.
Cœur
14

To jest błąd w zestawie internetowym . Chociaż zostało to rozwiązane na ich końcu, ale nie zostało jeszcze dostarczone z wersją iOS GM. Jedno z rozwiązań tego problemu:

(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
źródło
6

Wydaje się, że nie jest buforowana, jeśli zmienia się liczba elementów.
Tak mogłem tego uniknąć.

<!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
źródło