web-dev-qa-db-fra.com

L'état de la matrice sera mis en cache dans iOS 12 Safari. Est-ce un bug ou une fonctionnalité?

Mise à jour à 2018.10.31

Ce bogue a été corrigé sous iOS 12.1, passez une bonne journée ~

J'ai trouvé un problème avec l'état de la valeur de Array dans iOS 12 Safari, par exemple, avec un code comme celui-ci:

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

Après l'actualisation de la page, la valeur du tableau est toujours inversée. Est-ce un bug ou une fonctionnalité du nouveau Safari?


Voici une page de démonstration. Essayez de l'utiliser avec iOS 12 Safari: https://abelyao.github.io/others/ios12-safari-bug.html

432
abelyao

C'est définitivement un bogue! Et c'est un bug très grave.

Le bogue est dû à l'optimisation des initialiseurs de tableaux dans lesquels toutes les valeurs sont des littéraux primitifs. Par exemple, étant donné la fonction:

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

Toutes les références de tableau renvoyées des appels à buildArray() seront liées à la même mémoire, et certaines méthodes telles que toString() verront leurs résultats mis en cache. Normalement, pour préserver la cohérence, toute opération modifiable sur ces baies optimisées copiera les données dans un espace mémoire séparé et y sera liée; ce modèle s'appelle copy-on-write , ou CoW en abrégé.

La méthode reverse() mute le tableau, elle devrait donc déclencher une copie sur écriture. Mais ce n'est pas le cas, car l'implémenteur d'origine (Keith Miller de Apple) a raté le cas reverse(), alors qu'il avait écrit de nombreux cas de test.

Ce bug était rapporté à Apple le 21 août. Le correctif a atterri dans le référentiel WebKit le 27 août et expédié dans Safari 12.0.1 et iOS 12.1 le 30 octobre 2018.

271
hax

J'ai écrit une bibliothèque pour corriger le bogue. https://www.npmjs.com/package/array-reverse-polyfill

C'est le 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

Ceci est un bogue dans webkit . Bien que cela ait été résolu à la fin, mais pas encore livré avec iOS GM release. Une des solutions à ce problème:

(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

Il ne semble pas être mis en cache si le nombre d'éléments change.
J'ai pu éviter ça comme ça.

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