Due "sapori" di indefinito in JavaScript mostrato cercando di mapparli

5

Perché il 1 ° metodo map qui sotto elabora l'elemento # 2 come undefined ma il 2 ° metodo map no?

const a = [undefined,,42]; // Note the two consecutive commas.
console.log(a.map(x=>x));             // -> [undefined, undefined, 42]
console.log(a.map(x=>x===undefined)); // -> [true, undefined, false]

Espandilo con il codice qui sotto. Lì ho alcuni esempi numerati che illustrano i miei punti. In ogni caso creo un array a 3 elementi e da quel momento sono interessato solo al secondo elemento. Dimostrò che questo 2 ° elemento può essere considerato come undefined sia dal modo in cui risponde a .toString() (cioè come appare in console.log(elmt2) ) sia come si comporta in una dichiarazione di uguaglianza (cioè elmt2 === undefined ?) . Quindi, tuttavia, associo tutti gli elementi dell'array a una stringa costante indipendentemente da quali siano i valori (ad esempio array.map(_=>'foo') ) e osserviamo il 2 ° elemento del risultato mappato. Mi sarei aspettato che tutte le uscite fossero la stringa costante, ma non lo sono.

const check = (i, array) => {
  const mapped = array.map(() => 'mapped   ');
  const equalsUndefined = (array[1] === undefined);
  const mappable = (array.map(_=>1)[1] === 1);
  console.log('${i}:  ${array[1]}  ${equalsUndefined}     ${mapped[1]}    ${mappable}');
};

const uExplicit  = [4, undefined, 4][1];
const uEmptySlot = [4,          , 4][1];
const uNewArray  = (new Array(3)   )[1];

console.log('#:  origValue  =undef?  mappedValue  mappable?');
check(1, [4, undefined , 4]);
check(2, [4,           , 4]);
check(3, new Array(3)      );
check(4, [4, uExplicit , 4]);
check(5, [4, uEmptySlot, 4]);
check(6, [4, uNewArray , 4]);

/*
results:
#:  origValue  =undef?  mappedValue  mappable?
1:  undefined  true     mapped       true
2:  undefined  true     undefined    false
3:  undefined  true     undefined    false
4:  undefined  true     mapped       true
5:  undefined  true     mapped       true
6:  undefined  true     mapped       true
 */

La riga 1 mostra che un valore undefined esplicito può essere mappato correttamente in questo modo.

Tuttavia, le righe 2 e amp; 3 mostrano che gli elementi dell'array che possono essere considerati "vuoti" non possono essere mappati correttamente, anche se si comportano come undefined in base ai due test descritti sopra. Nella riga 2, il valore viene estratto da uno spazio vuoto letterale tra due elementi di matrice altrimenti normali. Nella riga 3, il valore viene estratto da una matrice creata utilizzando new Array(arrayLength) .

Stranamente, righe 3, 4 e amp; 5 mostrano che i valori esaminati nelle righe 1, 2 e amp; 3 rispettivamente possono essere mappati correttamente se vengono assegnati prima a una variabile, invece di essere usati direttamente.

Per essere chiari, la documentazione MDN per gli array afferma che new Array(arrayLength) crea "... una matrice di slot vuoti di arrayLength, non slot con valori non definiti reali". Tuttavia, l'output qui mostra che questi valori sono trattati come undefined , almeno rispetto a come si comportano con .toString() e in istruzioni di uguaglianza.

Quindi, ci sono due "sapori" diversi di undefined in JavaScript, uno che può essere manipolato usando map e uno che non può? Questo comportamento è previsto / benedetto (anche se è forse un po 'contro-intuitivo)? Esiste un modo sicuro per map attraverso un array in modo che tutti i valori di undefined possano essere trattati in modo identico?

UPDATE: THE SOURCE OF MY UNCERTAINTY

Ho scoperto perché ero perplesso. Si scopre che lo snippet di codice Stack Overflow produce un output ambiguo quando si utilizza console.log in questo caso, il che mi fa pensare che qualcosa fosse undefined quando in realtà era inesistente.

Per il semplice comando console.log([undefined,,4]) , le console di Chrome, Firefox, Safari e Opera producono output corretti e non ambigui:

Chrome:

Firefox:

Safari:

Opera:

Tuttavia,l'outputdallostrumentosnippetdicodicesuStackOverflow(inqualsiasibrowser)èambiguoealmenofuorviantesenontecnicamentenoncorretto(asecondadicomesidefiniscecosadovrebbefareconsole.log).(AncheseallafinehopostatoquestadomandasuSoftwareEngineering,chenonhalostrumentosnippetdicodice,inizialmentehorisoltoilproblemasuStackOverflow,cosachemihaportatoavederel'outputconfusomostratodiseguito.)Devodividerel'inputel'outputindueimmaginiseparatesoloperchésitrovanosupartidiversedelloschermo...

Inputdellosnippetdicodicedellostackoverflow:

Outputdellosnippetdicodicedellostackoverflow:

Sinoticheinquestauscitailprimoeilsecondoelementosonomostratiidenticamente,cioècomeundefined,incontrastocontuttiglioutputdellaconsoledelbrowsersopraiqualimostrailprimoelementocomeundefinedmal'elementonellasecondaposizionecomenon-existent.

Solopergliinteressidellepersone,notailseguentebuginInternetExplorer11quandoprovoamostrarelastessacosanellasuaconsole:

    
posta Andrew Willems 05.02.2017 - 02:48
fonte

2 risposte

7

Dovrei controllare le specifiche ECMAScript, ma sospetto che la situazione sia la seguente. Un array è più o meno come un oggetto con chiavi "integer" (più length che ignorerò). Uno "spazio vuoto" è semplicemente una chiave mancante, ovvero [1,,2] è come {"0":1,"2":2} . map è quindi come for(var key in a){ ... } .

Nel caso [4,undefined,4] hai {"0":4,"1":undefined,"2":4} e quindi mappare x => x === undefined su di esso porta a

{
    "0": 4 === undefined,
    "1": undefined === undefined,
    "2": 4 === undefined
}

che è {"0":false,"1":true,"2":false} i.e. [false,true,false] . D'altra parte, [4,,4] significa {"0":4,"2":4} e l'associazione su di essa porta a:

{
    "0": 4 === undefined,
    "2": 4 === undefined
}

che significa [false, undefined, false] . console.log(a) presumibilmente solo loop da 0 a a.length - 1 stampa di valori e {}.foo === undefined o più rilevanti {"0":false,"2":false}["1"] === undefined così undefined è ciò che viene visualizzato.

Questa interpretazione spiega tutti i risultati che vedi.

    
risposta data 05.02.2017 - 07:45
fonte
3

Questa è una conseguenza di come map gestisce gli slot mancanti in un array.

[undefined, , 42].map(x=>"hello") 

yields

["hello", undefined, "hello"]

Quando mancano "slot" nell'array di input, gli stessi slot ci mancheranno nell'array di output.

Da le specifiche :

callbackfn is called only for elements of the array which actually exist; it is not called for missing elements of the array.

In realtà la matrice ha solo due voci e la funzione di mappatura viene eseguita solo due volte. È solo quando la matrice viene stampata come una stringa che "gli slot mancanti" vengono stampati come "indefiniti". Avrebbe forse più senso se la funzione di stampa usasse la sintassi della virgola successiva e scrivesse:

["hello" , , "hello"]

Ma questa è solo una questione di come vengono stampati gli array. Usando questa convenzione il tuo esempio avrebbe più senso:

const a = [undefined,,42]; // Note the two consecutive commas.
console.log(a.map(x=>x));             // -> [undefined,,42]
console.log(a.map(x=>x===undefined)); // -> [true,,false]

La distinzione non è tra tipi di undefined , ma è tra uno slot di matrice non esistente e uno slot di matrice esistente con il valore undefined . Entrambi restituiscono undefined se chiedi il valore, ma internamente sono cose diverse.

Le matrici in JavaScript sono sparse , il che significa che uno slot "esiste" solo se è stato assegnato esplicitamente un valore. Assegnare undefined a uno slot significa comunque che lo slot è stato creato. Nella sintassi letterale dell'array, le virgole consecutive indicano che lo slot è mantenuto inesistente, mentre la parola chiave undefined causa la creazione dello slot e l'assegnazione del valore undefined .

    
risposta data 05.02.2017 - 15:21
fonte

Leggi altre domande sui tag