Status array akan di-cache di iOS 12 Safari. Apakah ini bug atau fitur?

432

Pembaruan di 2018.10.31

Bug ini telah diperbaiki di iOS 12.1, semoga harimu menyenangkan ~

Saya menemukan masalah dengan status nilai Array di iOS 12 Safari yang baru dirilis, misalnya, kode seperti ini:

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

Setelah menyegarkan halaman, nilai array masih terbalik. Apakah ini bug atau fitur Safari baru?


Ini halaman demo. Cobalah untuk menggunakannya dengan iOS 12 Safari: https://abelyao.github.io/others/ios12-safari-bug.html

abelyao
sumber
41
Bug dikonfirmasi juga di macOS 10.14 Mojave - i.imgur.com/ZJtJJC1.png
a_rahmanshah
43
macOS 10.13.6 (High Sierra) dengan Safari Versi 12.0 (13606.2.11) memiliki masalah yang sama. Array masih terbalik setelah menyegarkan halaman.
Kevin Gimbel
2
Bug telah diperbaiki di Safari 12.0.1 (macOS), serta di iOS 12.1.
MrMister

Jawaban:

272

Ini pasti BUG! Dan itu adalah bug yang sangat serius.

Bug ini disebabkan oleh optimalisasi inisialisasi array di mana semua nilai adalah literal primitif. Misalnya diberi fungsi:

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

Semua referensi array yang dikembalikan dari panggilan ke buildArray()akan ditautkan ke memori yang sama, dan beberapa metode seperti toString()akan menyimpan hasilnya. Biasanya, untuk menjaga konsistensi, setiap operasi yang dapat diubah pada array yang dioptimalkan akan menyalin data ke ruang memori yang terpisah dan menautkannya; pola ini disebut singkatnya copy-on-write , atau Kontrak Karya.

The reverse()metode bermutasi array, sehingga harus memicu copy-on-write. Tetapi tidak, karena implementor asli (Keith Miller dari Apple) melewatkan reverse()kasus ini, meskipun dia telah menulis banyak testcases.

Bug ini dilaporkan ke Apple pada 21 Agustus. Perbaikannya mendarat di repositori WebKit pada 27 Agustus dan dikirim di Safari 12.0.1 dan iOS 12.1 pada 30 Oktober 2018.

hax
sumber
11
Catatan: Safari 12.0 pada Mac OS X juga memiliki masalah yang sama.
hax
17
Ya itu sudah diperbaiki di sumbernya, dan sudah dikirim di Safari Technology Preview. Coba cdn.miss.cat/demo/ios12-safari-bug.html di Safari Technology Preview 65. Anda akan menemukan bahwa ia tidak memiliki bug.
sontonbarker
6
Saya tidak percaya penyebab mendasar dari bug adalah hasil dari pencampuran indeks; alih-alih, tampaknya disebabkan oleh kelalaian untuk memeriksa apakah suatu objek tidak dapat diubah sebelum memodifikasinya. Masalah slice mungkin memiliki penjelasan yang serupa, tetapi tidak sama tetapi dan tidak akan diperbaiki oleh patch untuk dibalik, sejauh yang saya tahu. Anda harus mempertimbangkan membuka laporan bug WebKit untuk masalah slice.
Zenexer
5
@ Zenexer Anda benar. Saya menulis jawaban ini sebelum saya menemukan bugs.webkit.org/show_bug.cgi?id=188794 dan melihat kode sumbernya. Saya akan mengedit jawaban saya.
hax
75

Saya menulis lib untuk memperbaiki bug. https://www.npmjs.com/package/array-reverse-polyfill

Ini kodenya :

(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
sumber
4
Perbarui kapan saja. Selamat berkontribusi.
Edire Fan
14
@zephi, saya kira penulisan length ( this.length = this.length) akan memicu Copy On Write, sehingga akan mengubah alamat memori array, dan juga akan memperbaiki perilaku reverse.
Cœur
14

Ini adalah bug di webkit . Meskipun ini telah dipecahkan pada akhirnya tetapi belum dikirim dengan rilis iOS GM. Salah satu solusi untuk masalah ini:

(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
sumber
6

Tampaknya tidak akan di-cache jika jumlah elemen berubah.
Saya bisa menghindari ini seperti ini.

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