Perché questa riga è valida in javascript?
var a = 0[0];
Dopo di che, aè undefined.
"0"di un new Number(0)oggetto.
0[0]restituire un valore
0["toString"]È fantastico, grazie per averlo fatto notare.
Perché questa riga è valida in javascript?
var a = 0[0];
Dopo di che, aè undefined.
"0"di un new Number(0)oggetto.
0[0]restituire un valore
0["toString"]È fantastico, grazie per averlo fatto notare.
Risposte:
Quando lo fai 0[0], l'interprete JS trasformerà il primo 0in un Numberoggetto e quindi proverà ad accedere alla [0]proprietà di quell'oggetto che èundefined .
Non ci sono errori di sintassi perché la sintassi di accesso alle proprietà 0[0]è consentita dalla grammatica della lingua in questo contesto. Questa struttura (utilizzando termini nella grammatica Javascript) èNumericLiteral[NumericLiteral] .
La parte rilevante della grammatica della lingua dalla sezione A.3 delle specifiche ECMAScript ES5 è questa:
Literal ::
NullLiteral
BooleanLiteral
NumericLiteral
StringLiteral
RegularExpressionLiteral
PrimaryExpression :
this
Identifier
Literal
ArrayLiteral
ObjectLiteral
( Expression )
MemberExpression :
PrimaryExpression
FunctionExpression
MemberExpression [ Expression ]
MemberExpression . IdentifierName
new MemberExpression Arguments
Quindi, si può seguire il grammatico attraverso questa progressione:
MemberExpression [ Expression ]
PrimaryExpression [ Expression ]
Literal [ Expression ]
NumericLiteral [ Expression ]
E, allo stesso modo, Expressionalla fine può anche essere NumericLiteralcosì dopo aver seguito la grammatica, vediamo che questo è permesso:
NumericLiteral [ NumericLiteral ]
Ciò significa che 0[0]è una parte consentita della grammatica e quindi nessun SyntaxError.
Quindi, in fase di esecuzione è consentito leggere una proprietà che non esiste (verrà semplicemente letta come undefined) fintanto che la fonte da cui si sta leggendo è un oggetto o ha una conversione implicita in un oggetto. Inoltre, un letterale numerico ha effettivamente una conversione implicita in un oggetto (un oggetto Number).
Questa è una di quelle caratteristiche spesso sconosciute di Javascript. I tipiNumber , BooleaneString in Javascript di solito sono memorizzati internamente come primitive (non oggetti in piena regola). Si tratta di una rappresentazione di archiviazione compatta e immutabile (probabilmente eseguita in questo modo per l'efficienza dell'implementazione). Tuttavia, Javascript vuole che tu sia in grado di trattare queste primitive come oggetti con proprietà e metodi. Quindi, se si tenta di accedere a una proprietà o un metodo che non è direttamente supportato sulla primitiva, Javascript forzerà temporaneamente la primitiva in un tipo appropriato di oggetto con il valore impostato sul valore della primitiva.
Quando si utilizza una sintassi simile a un oggetto su una primitiva come 0[0], l'interprete la riconosce come una proprietà di accesso su una primitiva. La sua risposta a questo è prendere la prima 0primitiva numerica e forzarla in un Numberoggetto in piena regola su cui può quindi accedere alla [0]proprietà. In questo caso specifico, la [0]proprietà su un oggetto Number èundefined è il motivo per cui questo è il valore da cui ottieni 0[0].
Ecco un articolo sulla conversione automatica di una primitiva in un oggetto allo scopo di trattare le proprietà:
La vita segreta delle primitive Javascript
Ecco le parti rilevanti della specifica ECMAScript 5.1:
Genera TypeError se il valore è undefinedo null, altrimenti restituisce true.

- Lascia che baseReference sia il risultato della valutazione di MemberExpression.
- Sia baseValue GetValue (baseReference).
- Lascia che propertyNameReference sia il risultato della valutazione di Expression.
- Lascia che propertyNameValue sia GetValue (propertyNameReference).
- Chiama CheckObjectCoercible (baseValue).
- Lascia che propertyNameString sia ToString (propertyNameValue).
- Se la produzione sintattica che viene valutata è contenuta in un codice in modalità rigorosa, sia vero rigoroso, altrimenti falso sia rigoroso.
- Restituisce un valore di tipo Reference il cui valore di base è baseValue e il cui nome di riferimento è propertyNameString e il cui flag di modalità rigorosa è rigoroso.
Una parte operativa di questa domanda è il passaggio n. 5 sopra.
Descrive come quando il valore a cui si accede è un riferimento a una proprietà, chiama ToObject(base)per ottenere la versione dell'oggetto di qualsiasi primitiva.
Questo descrive come Boolean, Numbere le Stringprimitive vengono convertite in un formato oggetto con la proprietà interna [[PrimitiveValue]] impostata di conseguenza.
Come test interessante, se il codice fosse così:
var x = null;
var a = x[0];
Continuerebbe a non lanciare un SyntaxError in fase di analisi poiché questa è tecnicamente una sintassi legale, ma genererebbe un TypeError in fase di esecuzione quando si esegue il codice perché quando la logica di Property Accessors sopra viene applicata al valore di x, chiamerà CheckObjectCoercible(x)o chiamerà ToObject(x)quale entrambi genereranno un'eccezione TypeError se xè nullo undefined.
0[1,2]è valido anche, cosa significa? (Aggiorno la domanda)
nullo undefinedva bene, anche se quelle proprietà non esistono.
0[2]
1,2ma restituisce 2.
Come la maggior parte dei linguaggi di programmazione, JS utilizza una grammatica per analizzare il codice e convertirlo in un formato eseguibile. Se non esiste una regola nella grammatica che può essere applicata a un particolare blocco di codice, genera un'eccezione SyntaxError. In caso contrario, il codice è considerato valido, indipendentemente dal fatto che abbia senso o meno.
Le parti rilevanti della grammatica JS sono
Literal ::
NumericLiteral
...
PrimaryExpression :
Literal
...
MemberExpression :
PrimaryExpression
MemberExpression [ Expression ]
...
Poiché è 0[0]conforme a queste regole, è considerata un'espressione valida . Che sia corretto (ad esempio, non genera un errore in fase di esecuzione) è un'altra storia, ma sì, lo è. Ecco come JS valuta espressioni come someLiteral[someExpression]:
someExpression(che può essere complesso arbitrario)Number, stringhe => Stringecc.)get propertyoperazione sul risultato (2) con il nome della proprietà risultato (1)Quindi 0[0]viene interpretato come
index = 0
temp = Number(0)
result = getproperty(temp, index) // it's undefined, but JS doesn't care
delete temp
return result
Ecco un esempio di un'espressione valida ma errata :
null[0]
Viene analizzato correttamente, ma in fase di esecuzione, l'interprete non riesce al passaggio 2 (perché nullnon può essere convertito in un oggetto) e genera un errore di runtime.
var x = null; var a = x[0];non genera un errore di sintassi, ma genera un'eccezione TypeError in fase di esecuzione.
0[0]restituire un valore invece di undefined
Ci sono situazioni in cui potresti validamente inserire un numero in Javascript:
-> 0['toString']
function toString() { [native code] }
Sebbene non sia immediatamente evidente il motivo per cui vorresti farlo, l'inserimento in Javascript equivale a usare la notazione puntata (anche se la notazione punto ti limita a usare identificatori come chiavi).
(0).toString(senza chiamare la funzione). È una proprietà del tipo di numero.
0si acceda alla sua proprietà e poiché non esiste, undefinedè più corretto come spiegato in jfriend00.
0[0]tornerà indefinito. È probabile che lo sarà, ma non deve essere così
Vorrei solo notare che questa è una sintassi valida non è in alcun modo unica per Javascript. La maggior parte delle lingue presenterà un errore di runtime o un errore di tipo, ma non è la stessa cosa di un errore di sintassi. Javascript sceglie di restituire undefined in molte situazioni in cui un'altra lingua potrebbe sollevare un'eccezione, incluso quando si indicizza un oggetto che non ha una proprietà del nome dato.
La sintassi non conosce il tipo di un'espressione (anche una semplice espressione come un letterale numerico) e ti consentirà di applicare qualsiasi operatore a qualsiasi espressione. Ad esempio, il tentativo di scrivere in apice undefinedo nullcausa un TypeErrorin Javascript. Non è un errore di sintassi: se questo non viene mai eseguito (essendo dalla parte sbagliata di un'istruzione if), non causerà alcun problema, mentre un errore di sintassi viene sempre rilevato per definizione in fase di compilazione (eval, Function, ecc. , contano tutti come compilazione).
Perché è una sintassi valida e persino un codice valido da interpretare. Puoi provare ad accedere a qualsiasi proprietà di qualsiasi oggetto (e in questo caso 0 verrà convertito in un oggetto Number), e ti darà il valore se esiste, altrimenti non definito. Tuttavia, il tentativo di accedere a una proprietà di undefined non funziona, quindi 0 [0] [0] comporterebbe un errore di runtime. Questo sarebbe comunque classificato come sintassi valida. C'è una differenza tra ciò che è una sintassi valida e ciò che non causerà errori di runtime / compiletime.
Non solo la sintassi è valida, ma il risultato non deve essere necessariamente undefinedanche se nella maggior parte dei casi, se non in tutti, lo sarà. JS è uno dei linguaggi orientati agli oggetti più puri. La maggior parte dei cosiddetti linguaggi OO sono orientati alla classe, nel senso che non è possibile modificare la forma (è legata alla classe) dell'oggetto una volta creato, ma solo lo stato dell'oggetto. In JS puoi cambiare lo stato e la forma dell'oggetto e questo lo fai più spesso di quanto pensi. Questa capacità rende un codice piuttosto oscuro, se lo usi in modo improprio. I numeri sono immutabili, quindi non puoi cambiare l'oggetto stesso, non il suo stato né la sua forma, quindi potresti farlo
0[0] = 1;
che è un'espressione di assegnazione valida che restituisce 1 ma in realtà non assegna nulla, il numero 0è immutabile. Il che di per sé è un po 'strano. Puoi avere un'espressione di valutazione valida e corretta (eseguibile), che non assegna nulla (*). Tuttavia, il tipo del numero è un oggetto modificabile, quindi puoi mutare il tipo e le modifiche verranno applicate a cascata lungo la catena del prototipo.
Number[0] = 1;
//print 1 to the console
console.log(0[0]);
//will also print 1 to the console because all integers have the same type
console.log(1[0]);
ovviamente è molto diverso dalla categoria d'uso sano, ma il linguaggio è specificato per consentire questo perché in altri scenari, estendere le capacità degli oggetti in realtà ha molto senso. È il modo in cui i plugin jQuery si collegano all'oggetto jQuery per fornire un esempio.
(*) Assegna effettivamente il valore 1 alla proprietà di un oggetto, tuttavia non è possibile fare riferimento a quell'oggetto (trasparente) e verrà quindi raccolto al nexx GC pass
In JavaScript, tutto è oggetto, quindi quando l'interprete lo analizza, tratta 0 come un oggetto e cerca di restituire 0 come una proprietà. La stessa cosa accade quando si tenta di accedere all'elemento 0 di true o "" (stringa vuota).
Anche se si imposta 0 [0] = 1, imposterà la proprietà e il suo valore in memoria, ma mentre si accede a 0 viene trattato come un numero (non confondere tra il trattamento come Oggetto e il numero qui.)
true[0]o""[0]