Pesquisa de site

javascript - O estado do array será armazenado em cache no Safari do iOS 12. É um bug ou recurso?


Atualização em 31.10.2018 :

Esse bug foi corrigido no iOS 12.1, tenha um bom dia~

Eu encontrei um problema com o estado do valor de Array no Safari do iOS 12 recém-lançado, por exemplo, 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>

Após atualizar a página, o valor do array ainda está invertido. Isso é um bug ou uma funcionalidade do novo Safari?


Aqui está uma página de demonstração. Tente usá-la com o Safari do iOS 12: https://abelyao.github.io/others/ios12-safari-bug.html

Resposta #1

Definitivamente é um BUG! E é um bug muito sério.

O bug se deve à otimização dos inicializadores de arrays nos quais todos os valores são literais primitivos. Por exemplo, dada a função:

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

Todas as referências de array retornadas por chamadas para buildArray() estarão ligadas à mesma memória, e alguns métodos como toString() terão seus resultados armazenados em cache. Normalmente, para preservar a consistência, qualquer operação mutável em tais arrays otimizados copiará os dados para um espaço de memória separado e se ligará a ele; esse padrão é chamado de copy-on-write, ou CoW para abreviar.

O método reverse() altera o array, então ele deveria acionar uma cópia por escrita. Mas não o faz, porque o implementador original (Keith Miller da Apple) perdeu o caso do reverse(), mesmo tendo escrito muitos casos de teste.

Este bug foi reportado à Apple em 21 de agosto. A correção foi incluída no repositório do WebKit em 27 de agosto e foi disponibilizada no Safari 12.0.1 e no iOS 12.1 em 30 de outubro de 2018.

Resposta #2

Eu escrevi uma biblioteca para corrigir o bug. https://www.npmjs.com/package/array-reverse-polyfill

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

Resposta #3

Este é um bug no webkit. Embora isso tenha sido resolvido do lado deles, ainda não foi lançado com a versão GM do iOS. Uma das soluções para esse 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;
    }
  }
})();

Artigos relacionados