Oggetti Simili agli Array in JavaScript

Concetti Chiave
  • In JavaScript, gli oggetti simili agli array sono oggetti che hanno una proprietà length e proprietà numeriche non negative, ma non ereditano da Array.prototype.
  • Questi oggetti possono essere iterati come array, ma non hanno accesso diretto ai metodi degli array come map, filter o reduce.
  • Possiamo utilizzare il metodo Array.from() per convertire un oggetto simile agli array in un vero array, permettendo l'uso dei metodi degli array su di esso.
  • Gli oggetti simili agli array sono utili in situazioni in cui abbiamo bisogno di trattare un oggetto come un array, ma non abbiamo bisogno di tutte le funzionalità degli array JavaScript.
  • Esempi comuni di oggetti simili agli array includono i risultati di query DOM, gli argomenti delle funzioni e gli array-like restituiti da alcune API JavaScript.

Oggetti Simili agli Array

Come abbiamo visto, gli array JavaScript hanno alcune caratteristiche speciali che altri oggetti non hanno:

  • La proprietà length viene automaticamente aggiornata quando nuovi elementi vengono aggiunti alla lista.
  • Impostare length a un valore più piccolo di quello attuale tronca l'array.
  • Gli array ereditano metodi utili da Array.prototype.
  • Array.isArray() restituisce true per gli array.

Queste sono le caratteristiche che rendono gli array JavaScript distinti dagli oggetti regolari. Ma non sono le caratteristiche essenziali che definiscono un array. È spesso perfettamente ragionevole trattare qualsiasi oggetto con una proprietà numerica length e corrispondenti proprietà intere non negative come una sorta di array.

Questi oggetti "simili agli array" compaiono effettivamente occasionalmente nella pratica, e sebbene non si possano invocare direttamente i metodi dell'array su di essi o aspettarsi un comportamento speciale dalla proprietà length, si può ancora iterare attraverso di essi con lo stesso codice che si userebbe per un vero array. Si scopre che molti algoritmi per array funzionano altrettanto bene con oggetti simili agli array come fanno con array reali. Questo è specialmente vero se gli algoritmi in questione trattano l'array come di sola lettura o se almeno lasciano invariata la lunghezza dell'array.

Il seguente codice prende un oggetto regolare, aggiunge proprietà per renderlo un oggetto simile a un array, e poi itera attraverso gli "elementi" del pseudo-array risultante:

// Iniziamo con un oggetto vuoto.
let a = {};

// Aggiungiamo proprietà numeriche e una proprietà length.
let i = 0;
while(i < 10) {
    a[i] = i * i;
    i++;
}
a.length = i;

// Ora iteriamo attraverso le proprietà numeriche dell'oggetto.
let totale = 0;
for(let j = 0; j < a.length; j++) {
    totale += a[j];
}

In JavaScript lato client, diversi metodi per lavorare con documenti HTML (come document.querySelectorAll(), per esempio) restituiscono oggetti simili agli array. Ecco una funzione che si potrebbe adoperare per testare oggetti che funzionano come array:

// Determina se o è un oggetto simile a un array.
// Le stringhe e le funzioni hanno proprietà length numeriche, ma sono
// escluse dal test typeof. In JavaScript lato client, i nodi di testo DOM
// hanno una proprietà length numerica, e potrebbero dover essere esclusi
// con un test aggiuntivo o.nodeType !== 3.
function èSimileAArray(o) {
    if (o &&                            // o non è null, undefined, ecc.
        typeof o === "object" &&        // o è un oggetto
        Number.isFinite(o.length) &&    // o.length è un numero finito
        o.length >= 0 &&                // o.length è non negativo
        Number.isInteger(o.length) &&   // o.length è un intero
        o.length < 4294967295) {        // o.length < 2^32 - 1
        return true;                    // Allora o è simile a un array.
    } else {
        return false;                   // Altrimenti non lo è.
    }
}

Vedremo nella prossima lezione che le stringhe si comportano come array. Tuttavia, test come questo per oggetti simili agli array tipicamente restituiscono false per le stringhe. Le stringhe, infatti, sono solitamente gestite meglio come stringhe, non come array.

La maggior parte dei metodi array JavaScript sono appositamente definiti per essere generici in modo che funzionino correttamente quando applicati a oggetti simili agli array in aggiunta ai veri array. Poiché gli oggetti simili agli array non ereditano da Array.prototype, non si possono invocare i metodi array su di essi direttamente. Si possono invocare indirettamente, tuttavia, usando il metodo Function.call (che vedremo più avanti):

// Un oggetto simile a un array
let a = {
    "0": "a",
    "1": "b",
    "2": "c",
    length: 3
};

// Usa i metodi dell'array su di esso
Array.prototype.join.call(a, "+"); // => "a+b+c"
Array.prototype.map.call(a, x => x.toUpperCase()); // => ["A","B","C"]
Array.prototype.slice.call(a, 0);   // => ["a","b","c"]: copia di array versione legacy
Array.from(a);                      // => ["a","b","c"]: copia di array più facile

La penultima riga di questo codice invoca il metodo Array slice() su un oggetto simile a un array per copiare gli elementi di quell'oggetto in un vero oggetto array. Questo è un trucco idiomatico che esiste in molto codice legacy, ma ora è molto più facile da fare con Array.from().