Le matrici vuote sembrano uguali vero e falso allo stesso tempo


201

Le matrici vuote sono vere ma sono uguali a false.

var arr = [];
console.log('Array:', arr);
if (arr) console.log("It's true!");
if (arr == false) console.log("It's false!");
if (arr && arr == false) console.log("...what??");

Immagino che ciò sia dovuto alla conversione implicita operata dall'operatore di uguaglianza.

Qualcuno può spiegare cosa sta succedendo dietro le quinte?


1
Ecco un thread simile che dovrebbe far luce sulla questione: stackoverflow.com/questions/4226101/…
Rion Williams

2
Si noti che arr == trueciò non è vero ;-)
Michael Krelin - hacker

5
Wow ... proprio quando pensavi di avere tutto questo giù.
Harpo,

3
Per evitare la coercizione di tipo Javascript WTF, utilizzare l'opeartor dell'uguaglianza rigorosa ===. Quindi, se si desidera verificare il vuoto di un array, utilizzarearr === []
DjebbZ il

17
Se si desidera verificare il vuoto di un array NON utilizzare arr === [], poiché restituirà SEMPRE falso, poiché il lato destro è un'istanza di un nuovo array e la variabile a sinistra non può fare riferimento a qualcosa appena creato. Il test del vuoto dovrebbe essere fatto guardando in alto arr.length === 0.
Kyle Baker,

Risposte:


274

Stai testando cose diverse qui.

if (arr) chiamato su oggetto (l'array è un'istanza di Object in JS) verificherà se l'oggetto è presente e restituisce vero / falso.

Quando si chiama if (arr == false)si confrontano i valori di questo oggetto e il falsevalore di base. Internamente, arr.toString()viene chiamato, che restituisce una stringa vuota "".

Questo perché toStringchiamato su Array restituisce Array.join()e la stringa vuota è uno dei valori falsi in JavaScript.


2
Puoi spiegare perché Boolean([])ritorna true?
Devy,

11
questo è per convenzione, in JS se gli oggetti sono costretti a booleani, sono sempre costretti a VERO. guarda la tabella "Contesto booleano" su: javascript.info/tutorial/object-conversion
Niki

2
@Devy tutti gli oggetti in JavaScript sono veritieri, quindi la conversione di qualsiasi oggetto in booleano è vera. Vedi 2ality.com/2013/08/objects-truthy.html
Thomson

62

Per quanto riguarda la linea:

if (arr == false) console.log("It's false!");

Forse questi aiuteranno:

console.log(0 == false) // true
console.log([] == 0) // true
console.log([] == "") // true

Quello che credo stia accadendo è che il booleano falsesia costretto a 0confrontarsi con un oggetto (il lato sinistro). L'oggetto è costretto a una stringa (la stringa vuota). Quindi, anche la stringa vuota viene forzata in un numero, ovvero zero. E così il confronto finale è 0== 0, che è true.

Modifica: vedere questa sezione delle specifiche per i dettagli su come funziona esattamente.

Ecco cosa sta succedendo, a partire dalla regola # 1:

1. Se Tipo (x) è diverso da Tipo (y), andare al passaggio 14.

La prossima regola applicabile è # 19:

19. Se Tipo (y) è Booleano, restituisce il risultato del confronto x == ToNumber (y).

Il risultato di ToNumber(false)è 0, quindi ora abbiamo:

[] == 0

Ancora una volta, la regola n. 1 ci dice di saltare al passaggio n. 14, ma il passaggio successivo che si applica effettivamente è il n. 21:

21. Se Tipo (x) è Oggetto e Tipo (y) è Stringa o Numero, restituisce il risultato del confronto ToPrimitive (x) == y.

Il risultato di ToPrimitive([])è la stringa vuota, quindi ora abbiamo:

"" == 0

Ancora una volta, la regola n. 1 ci dice di saltare al passaggio n. 14, ma il passaggio successivo che si applica effettivamente è il n. 17:

17. Se Tipo (x) è Stringa e Tipo (y) è Numero, restituisce il risultato del confronto ToNumber (x) == y.

Il risultato di ToNumber("")è 0, che ci lascia con:

0 == 0

Ora, entrambi i valori hanno lo stesso tipo, quindi i passaggi continuano dal # 1 al # 7, che dice:

7. Se x ha lo stesso valore numerico di y, restituisce true.

Quindi, torniamo true.

In breve:

ToNumber(ToPrimitive([])) == ToNumber(false)

2
Ottimo riferimento! Per evitare confusione, potrebbe essere utile menzionare che il motivo "la prossima regola applicabile è # 19" anche se la regola 1 dice "vai al passaggio 14", è perché i passaggi 14-18 non corrispondono ai tipi di valori confrontati.
Sean the Bean,

2
Bella spiegazione. Mi sorprende che le matrici vuote siano considerate veritiere, 0 sia falsa, eppure [] == 0sia vera. Capisco come ciò avvenga sulla base della tua spiegazione delle specifiche, ma sembra un comportamento linguistico strano dal punto di vista logico.
bigh_29,

7

Per integrare la risposta di Wayne e cercare di spiegare perché i ToPrimitive([])ritorni "", vale la pena considerare due possibili tipi di risposte alla domanda "perché". Il primo tipo di risposta è: "perché la specifica dice che questo è il comportamento di JavaScript". Nella specifica ES5, sezione 9.1 , che descrive il risultato di ToPrimitive come valore predefinito per un oggetto:

Il valore predefinito di un oggetto viene recuperato chiamando il metodo interno [[DefaultValue]] dell'oggetto, passando il suggerimento opzionale PreferredType.

La sezione 8.12.8 descrive il [[DefaultValue]]metodo. Questo metodo accetta un "suggerimento" come argomento e il suggerimento può essere String o Number. Per semplificare la questione dispensando alcuni dettagli, se il suggerimento è String, [[DefaultValue]]restituisce il valore di toString()se esiste e restituisce un valore di base e altrimenti restituisce il valore di valueOf(). Se il suggerimento è Numero, le priorità di toString()e valueOf()vengono invertite in modo che valueOf()venga chiamato per primo e il suo valore restituito se è una primitiva. Pertanto, se [[DefaultValue]]restituisce il risultato toString()o valueOf()dipende dal PreferredType specificato per l'oggetto e se queste funzioni restituiscono o meno valori primitivi.

Il valueOf()metodo Object predefinito restituisce semplicemente l'oggetto stesso, il che significa che, a meno che una classe non sostituisca il metodo predefinito, valueOf()restituisca l'oggetto stesso. Questo è il caso di Array. [].valueOf()restituisce l'oggetto []stesso. Poiché un Arrayoggetto non è una primitiva, il [[DefaultValue]]suggerimento è irrilevante: il valore restituito per un array sarà il valore di toString().

Per citare JavaScript di David Flanagan : The Definitive Guide , che, a proposito, è un libro eccezionale che dovrebbe essere il primo posto di tutti per ottenere risposte a questo tipo di domande:

I dettagli di questa conversione da oggetto a numero spiegano perché un array vuoto si converte nel numero 0 e perché un array con un singolo elemento può anche convertire in un numero. Le matrici ereditano il metodo valueOf () predefinito che restituisce un oggetto anziché un valore primitivo, quindi la conversione da array a numero si basa sul metodo toString (). Le matrici vuote vengono convertite nella stringa vuota. E la stringa vuota viene convertita nel numero 0. Un array con un singolo elemento viene convertito nella stessa stringa di quell'elemento. Se un array contiene un singolo numero, quel numero viene convertito in una stringa e quindi di nuovo in un numero.

Il secondo tipo di risposta alla domanda "why", oltre a "perché dice la specifica", fornisce una spiegazione del perché il comportamento ha senso dal punto di vista del design. Su questo problema posso solo speculare. Innanzitutto, come si converte un array in un numero? L'unica ragionevole possibilità a cui riesco a pensare sarebbe quella di convertire un array vuoto in 0 e qualsiasi array non vuoto in 1. Ma come ha rivelato la risposta di Wayne, un array vuoto verrà convertito in 0 per molti tipi di confronti. Oltre a ciò, è difficile pensare a un valore di ritorno primitivo ragionevole per Array.valueOf (). Quindi si potrebbe sostenere che ha più senso Array.valueOf()essere l'impostazione predefinita e restituire l'array stesso, portando toString()al risultato utilizzato da ToPrimitive. È più sensato convertire un array in una stringa, anziché in un numero.

Inoltre, come suggerito dalla citazione di Flanagan, questa decisione progettuale consente alcuni tipi di comportamenti benefici. Per esempio:

var a = [17], b = 17, c=1;
console.log(a==b);      // <= true
console.log(a==c);      // <= false

Questo comportamento consente di confrontare un array a singolo elemento con i numeri e ottenere il risultato previsto.


Grazie per questa risposta, questa è una spiegazione abbastanza dettagliata che manca alla domanda.
Estus Flask,

3
console.log('-- types: undefined, boolean, number, string, object --');
console.log(typeof undefined);  // undefined
console.log(typeof null);       // object
console.log(typeof NaN);        // number
console.log(typeof false);      // boolean
console.log(typeof 0);          // number
console.log(typeof "");         // string
console.log(typeof []);         // object
console.log(typeof {});         // object

console.log('-- Different values: NotExist, Falsy, NaN, [], {} --');
console.log('-- 1. NotExist values: undefined, null have same value --');
console.log(undefined == null); // true

console.log('-- 2. Falsy values: false, 0, "" have same value --');
console.log(false == 0);        // true
console.log(false == "");       // true
console.log(0 == "");           // true

console.log('-- 3. !NotExist, !Falsy, and !NaN return true --');
console.log(!undefined);        // true
console.log(!null);             // true

console.log(!false);            // true
console.log(!"");               // true
console.log(!0);                // true

console.log(!NaN);              // true

console.log('-- 4. [] is not falsy, but [] == false because [].toString() returns "" --');
console.log(false == []);       // true
console.log([].toString());     // ""

console.log(![]);               // false

console.log('-- 5. {} is not falsy, and {} != false, because {}.toString() returns "[object Object]" --');
console.log(false == {});       // false
console.log({}.toString());     // [object Object]

console.log(!{});               // false

console.log('-- Comparing --');
console.log('-- 1. string will be converted to number or NaN when comparing with a number, and "" will be converted to 0 --');
console.log(12 < "2");          // false
console.log("12" < "2");        // true
console.log("" < 2);            // true

console.log('-- 2. NaN can not be compared with any value, even if NaN itself, always return false --');
console.log(NaN == NaN);        // false

console.log(NaN == null);       // false
console.log(NaN == undefined);  // false
console.log(0 <= NaN);          // false
console.log(0 >= NaN);          // false
console.log(undefined <= NaN);  // false
console.log(undefined >= NaN);  // false
console.log(null <= NaN);       // false
console.log(null >= NaN);       // false

console.log(2 <= "2a");         // false, since "2a" is converted to NaN
console.log(2 >= "2a");         // false, since "2a" is converted to NaN

console.log('-- 3. undefined can only == null and == undefined, and can not do any other comparing even if <= undefined --');
console.log(undefined == null);         // true
console.log(undefined == undefined);    // true

console.log(undefined == "");           // false
console.log(undefined == false);        // false
console.log(undefined <= undefined);    // false
console.log(undefined <= null);         // false
console.log(undefined >= null);         // false
console.log(0 <= undefined);            // false
console.log(0 >= undefined);            // false

console.log('-- 4. null will be converted to "" when <, >, <=, >= comparing --');
console.log(12 <= null);        // false
console.log(12 >= null);        // true
console.log("12" <= null);      // false
console.log("12" >= null);      // true

console.log(0 == null);         // false
console.log("" == null);        // false

console.log('-- 5. object, including {}, [], will be call toString() when comparing --');
console.log(12 < {});           // false, since {}.toString() is "[object Object]", and then converted to NaN
console.log(12 > {});           // false, since {}.toString() is "[object Object]", and then converted to NaN
console.log("[a" < {});         // true, since {}.toString() is "[object Object]"
console.log("[a" > {});         // false, since {}.toString() is "[object Object]"
console.log(12 < []);           // false, since {}.toString() is "", and then converted to 0
console.log(12 > []);           // true, since {}.toString() is "", and then converted to 0
console.log("[a" < []);         // false, since {}.toString() is ""
console.log("[a" > []);         // true, since {}.toString() is ""

console.log('-- 6. According to 4 and 5, we can get below weird result: --');
console.log(null < []);         // false
console.log(null > []);         // false
console.log(null == []);        // false
console.log(null <= []);        // true
console.log(null >= []);        // true

2

In if (arr), viene sempre valutato (ToBoolean) su true se arr è un oggetto perché tutti gli oggetti in JavaScript sono veritieri . (null non è un oggetto!)

[] == falseviene valutato con un approccio iterativo. All'inizio, se un lato di ==è primitivo e l'altro è oggetto, all'inizio converte l'oggetto in primitivo, quindi converte entrambi i lati in Numero se entrambi i lati non lo sono string(il confronto delle stringhe viene utilizzato se entrambi i lati sono stringhe). Quindi il confronto è ripetuto come, [] == false-> '' == false-> 0 == 0-> true.


2

Esempio:

const array = []
const boolValueOfArray = !!array // true

Succede perché

ToNumber(ToPrimitive([])) == ToNumber(false)  
  1. []è un Arrayoggetto vuoto → ToPrimitive([])→ "" → ToNumber("")0
  2. ToNumber(false) → 0
  3. 0 == 0 → vero

1

Un array con elementi (a prescindere se 0, false o un altro array vuoto), risolve sempre l' trueutilizzo di Abstract Equality Comparison ==.

1. [] == false; // true, because an empty array has nothing to be truthy about
2. [2] == false; // false because it has at least 1 item
3. [false] == false; // also false because false is still an item
4. [[]] == false; // false, empty array is still an item

Ma usando un rigoroso confronto sull'uguaglianza ===, stai tentando di valutare il contenuto della variabile e il suo tipo di dati, ecco perché:

1. [] === false; // false, because an array (regardless of empty or not) is not strictly comparable to boolean `false`
2. [] === true; // false, same as above, cannot strictly compare [] to boolean `true`
3. [[]] === false; // true, because see #1

-1

È possibile svuotare un array JavaScript facendo riferimento a un nuovo array, utilizzando list = []o eliminando gli elementi dell'array attualmente indicato list.length = 0.

Fonte: JavaScript Empty Array


-2

Nessuna delle precedenti mi ha aiutato, quando ho provato ad usare il plugin di mappatura knockout.js, forse perché un "array vuoto" non è veramente vuoto.

Ho finito per usare: data-bind="if: arr().length"che ha fatto il trucco.

Questo è specifico del knockout, non della domanda del PO, ma forse aiuterà qualcun altro a navigare qui in una situazione simile.


Questa risposta non è correlata
fauverism il

solo tangenzialmente, come
reclamato
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.