El estado de la matriz se almacenará en caché en iOS 12 Safari. ¿Es un error o una característica?

432

Actualización a 2018.10.31

Este error se ha solucionado en iOS 12.1, tenga un buen día ~

Encontré un problema con el estado del valor de Array en el Safari iOS 12 recién lanzado, por ejemplo, un código como este:

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

Después de actualizar la página, el valor de la matriz aún se invierte. ¿Es esto un error o una característica del nuevo Safari?


Aquí hay una página de demostración. Intenta usarlo con iOS 12 Safari: https://abelyao.github.io/others/ios12-safari-bug.html

abelyao
fuente
41
Error confirmado también en macOS 10.14 Mojave - i.imgur.com/ZJtJJC1.png
a_rahmanshah
43
macOS 10.13.6 (High Sierra) con Safari versión 12.0 (13606.2.11) tiene el mismo problema. La matriz todavía se invierte después de actualizar la página.
Kevin Gimbel
2
El error se ha corregido en Safari 12.0.1 (macOS), así como en iOS 12.1.
MrMister

Respuestas:

272

Definitivamente es un error! Y es un error muy grave.

El error se debe a la optimización de los inicializadores de matriz en los que todos los valores son literales primitivos. Por ejemplo, dada la función:

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

Todas las referencias de matriz devueltas de las llamadas a buildArray()se vincularán a la misma memoria, y algunos métodos como toString()tendrán sus resultados en caché. Normalmente, para preservar la coherencia, cualquier operación mutable en dichos arreglos optimizados copiará los datos en un espacio de memoria separado y se vinculará a ellos; Este patrón se llama copy-on-write o CoW para abreviar.

El reverse()método muta la matriz, por lo que debería desencadenar una copia en escritura. Pero no es así, porque el implementador original (Keith Miller de Apple) perdió el reverse()caso, a pesar de que había escrito muchos casos de prueba.

Este error se informó a Apple el 21 de agosto. La solución aterrizó en el repositorio de WebKit el 27 de agosto y se envió a Safari 12.0.1 y iOS 12.1 el 30 de octubre de 2018.

hax
fuente
11
Nota: Safari 12.0 en Mac OS X también tiene el mismo problema.
hax
17
Sí, ya se ha solucionado en las fuentes y ya se envió en Safari Technology Preview. Pruebe cdn.miss.cat/demo/ios12-safari-bug.html en Safari Technology Preview 65. Verá que no tiene el error.
sidehowbarker
66
No creo que la causa subyacente del error sea el resultado de una confusión de índices; en cambio, parece ser causado por no verificar si un objeto es inmutable antes de modificarlo. El problema de corte puede tener una explicación similar, pero no es lo mismo, pero no se solucionará con el parche para revertir, por lo que puedo decir. Debería considerar abrir un informe de error de WebKit para el problema del segmento.
Zenexer
55
@ Zenexer Tienes razón. Escribí esta respuesta antes de encontrar bugs.webkit.org/show_bug.cgi?id=188794 y ver el código fuente. Editaré mi respuesta.
hax
75

Escribí una lib para corregir el error. https://www.npmjs.com/package/array-reverse-polyfill

Este es el código :

(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
fuente
44
Actualiza en cualquier momento. Bienvenido a contribuir.
Edire Fan el
14
@zephi, supongo que escribir en length ( this.length = this.length) activará Copy On Write, por lo que cambiará la dirección de memoria de la matriz y solucionará el comportamiento de reverse.
Cœur
14

Este es un error en el kit web . Aunque esto se resolvió al final, pero aún no se envió con el lanzamiento de iOS GM. Una de las soluciones a este problema:

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

Parece que no se almacena en caché si cambia el número de elementos.
Pude evitar esto así.

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