Le nuove versioni dei browser, dei sistemi operativi o semplicemente degli aggiornamenti di un software portano spesso con loro dei bug e dei difetti che vanno individuati tempestivamente prima che le cose possano farsi problematiche. Questo è il caso di Safari su iOS versione 12, dove il metodo reverse
dell'oggetto Array
non si comporta come ci si potrebbe aspettare.
Quello postato dall'utente abelyao su Stack Overflow descrive infatti un bug molto subdolo, che si palesa utilizzando il metodo Array.prototype.reverse
in un contesto come il seguente:
<!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>
Dopo che si effettua il refresh sulla pagina infatti, il valore dell'array rimane invertito. In questa pagina di demo è possibile testare quanto accade, ovviamente utilizzando Safari su iOS. L'errore è stato ufficializzato su Bugs.webkit.org.
Esso potrebbe essere provocato dalla procedura di ottimizzazione degli inizializzatori degli array, in cui tutti i valori sono valori primitivi. Come giustamente sottolineato dall'utente hax, per esempio, il codice () => [1, null, 'x']
restituisce dei riferimenti che linkano alla stessa locazione di memoria. Normalmente, le operazioni di modifica sugli array restituiti copiano i dati in uno spazio di memoria apposito e separato, collegandosi ad esso (copy on write). Il metodo reverse
invece, presenta un bug in questa fase.
Ovviamente, in attesa di una risoluzione ufficiale, esistono già svariate soluzioni applicabili sull'inconveniente, come questo pacchetto postato su npm:
(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);
}
})();
O anche attraverso il seguente snippet:
(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;
}
}
})();
Le operazioni con gli array sono di fondamentale importanza, e un bug su funzionalità native è da considerarsi grave allo stato attuale delle cose. Per questo motivo, tra alcuni sviluppatori, è iniziata a circolare la dicitura "Safari is the new IE"...