0 [0] sözdizimsel olarak neden geçerlidir?


119

Bu satır javascript'te neden geçerli?

var a = 0[0];

Bundan sonra aise undefined.


4
as true[0]or ""[0]
Hacketo

24
@CodeAngry Adil olmak gerekirse JavaScript HTML içinde doğdu ve "bana sevdiğin her şeyi at, ben de bunu anlamaya çalışacağım" olayını başlatan HTML'dir.
Niet the Dark Absol

8
@NiettheDarkAbsol Adil olmak gerekirse size sözdizimi olarak basitçe yanlış yapar mantıklı (henüz çok değil). Bu sadece "0"bir new Number(0)nesnenin adını almaktır.
meandre

a'nın her zaman tanımsız olacağı yanlış bir varsayımdır. 0[0]Bir değer döndürmek tamamen mümkün
Rune FS

@meandre 0["toString"]Bu harika, işaret ettiğiniz için teşekkürler.
Jonathan

Yanıtlar:


169

Bunu yaptığınızda 0[0], JS yorumlayıcısı ilkini 0bir Numbernesneye dönüştürür ve sonra [0]o nesnenin özelliğine erişmeye çalışır undefined.

0[0]Bu bağlamda özellik erişimi sözdizimine dil grameri tarafından izin verildiği için sözdizimi hatası yoktur . Bu yapı (Javascript dilbilgisindeki terimleri kullanarak) NumericLiteral[NumericLiteral].

ES5 ECMAScript spesifikasyonunun A.3 bölümündeki dil gramerinin ilgili kısmı şudur:

Literal ::
    NullLiteral
    BooleanLiteral
    NumericLiteral
    StringLiteral
    RegularExpressionLiteral

PrimaryExpression :
    this
    Identifier
    Literal
    ArrayLiteral
    ObjectLiteral
    ( Expression )

MemberExpression :
    PrimaryExpression
    FunctionExpression
    MemberExpression [ Expression ]
    MemberExpression . IdentifierName
    new MemberExpression Arguments    

Yani, bu ilerlemeyle grameri takip edebilirsiniz:

MemberExpression [ Expression ]
PrimaryExpression [ Expression ]
Literal [ Expression ]
NumericLiteral [ Expression ]

Ve benzer Expressionşekilde NumericLiteral, dilbilgisini takip ettikten sonra sonunda öyle olabilir , buna izin verildiğini görüyoruz:

NumericLiteral [ NumericLiteral ]

Bu 0[0], dilbilgisinin izin verilen bir parçası olduğu ve dolayısıyla SyntaxError olmadığı anlamına gelir .


Daha sonra, çalışma zamanında undefined, okuduğunuz kaynak bir nesne olduğu veya bir nesneye örtük bir dönüşümü olduğu sürece, var olmayan (yalnızca olarak okunacak ) bir özelliği okuyabilirsiniz . Ve sayısal bir değişmez, gerçekten de bir nesneye (bir Number nesnesi) örtük bir dönüşüme sahiptir.

Bu, Javascript'in genellikle bilinmeyen özelliklerinden biridir. Türleri Number, Booleanve StringJavaScript genellikle ilkel (değil tam gelişmiş nesneler) olarak saklanır. Bunlar kompakt, değişmez depolama temsilleridir (muhtemelen uygulama verimliliği için bu şekilde yapılmıştır). Ancak Javascript, bu ilkelleri özelliklere ve yöntemlere sahip nesneler gibi ele alabilmenizi ister. Dolayısıyla, ilkelde doğrudan desteklenmeyen bir özelliğe veya yönteme erişmeye çalışırsanız, Javascript geçici olarak ilkeli, değeri ilkel değerine ayarlanmış uygun bir nesne türüne zorlayacaktır.

İlkel gibi bir ilkelde nesne benzeri bir sözdizimi kullandığınızda 0[0], yorumlayıcı bunu bir ilkelde özellik erişimi olarak tanır. Buna cevabı, ilk 0sayısal ilkeli almak ve Numberonu, daha sonra [0]özelliğe erişebileceği tam gelişmiş bir nesneye zorlamaktır . Bu özel durumda, [0]Number nesnesindeki özellik undefinedbu yüzden aldığınız değerdir 0[0].

Burada, özelliklerle uğraşmak amacıyla bir ilkelin bir nesneye otomatik olarak dönüştürülmesiyle ilgili bir makale yer almaktadır:

Javascript İlkellerinin Gizli Yaşamı


ECMAScript 5.1 spesifikasyonunun ilgili bölümleri şunlardır:

9.10 CheckObjectCoercible

Değer undefinedveya ise TypeError atar null, aksi takdirde döndürür true.

görüntü açıklamasını buraya girin

11.2.1 Mülk Erişimcileri

  1. BaseReference, MemberExpression'u değerlendirmenin sonucu olsun.
  2. BaseValue GetValue (baseReference) olsun.
  3. PropertyNameReference, İfade değerlendirmesinin sonucu olsun.
  4. PropertyNameValue GetValue (propertyNameReference) olsun.
  5. CheckObjectCoercible (baseValue) öğesini çağırın.
  6. PropertyNameString'in ToString (propertyNameValue) olmasına izin verin.
  7. Değerlendirilen sözdizimsel üretim katı mod kodunda yer alıyorsa, katı doğru olsun, aksi takdirde katı yanlış olsun.
  8. Temel değeri baseValue olan ve başvurulan adı propertyNameString olan ve katı mod bayrağı katı olan Reference türünde bir değer döndürür.

Bu soru için etkin bir kısım yukarıdaki 5. adımdır.

8.7.1 GetValue (V)

Bu, erişilen değerin bir özellik referansı olduğunda, ToObject(base)herhangi bir ilkelin nesne sürümünü almayı nasıl çağırdığını açıklar .

9.9 Hedef

Bu Boolean, buna göre ayarlanmış [[İlkelDeğer]] dahili özellik ile nasıl Numberve Stringilkellerin bir nesne formuna dönüştürüldüğünü açıklar .


İlginç bir test olarak, kod böyle olsaydı:

var x = null;
var a = x[0];

Bu teknik olarak yasal sözdizimi olarak hala ayrıştırma anda SyntaxError atmak olmaz, ancak kodu çalıştırdığınızda Mülkiyet Erişimciler mantığı yukarıda değerine uygulandığında çünkü zamanında bir TypeError atmak istiyorum x, bu arayacak CheckObjectCoercible(x)veya çağrı ToObject(x)hangi eğer her ikisi de bir TypeError atacağım xolduğu nullveya undefined.


0[1,2]da geçerli, bu ne anlama geliyor? (Soruyu güncelliyorum)
Michael M.

Ve bir sözdizimi hatası doğurmaz, çünkü özelliklere erişim olmayan nullveya undefinedtamamen iyi olmayan herhangi bir şeyde, bu özellikler olmasa bile.
user4642212

6
@Michael'in güncellenmesine gerek yok. Bu virgül operatörü, yani sadece0[2]
Amit Joki

1
Virgül operatörü: hem 1 hem de 2'yi değerlendirir 1,2ancak 2
değerini

2
Bu, bir sorunun nüansı için harika bir cevap.
tbh__

20

Çoğu programlama dili gibi, JS de kodunuzu ayrıştırmak ve çalıştırılabilir bir forma dönüştürmek için bir dilbilgisi kullanır. Dilbilgisinde belirli bir kod yığınına uygulanabilecek bir kural yoksa, bir SyntaxError atar. Aksi takdirde, mantıklı olup olmadığına bakılmaksızın kod geçerli kabul edilir.

JS dilbilgisinin ilgili bölümleri şunlardır:

Literal :: 
   NumericLiteral
   ...

PrimaryExpression :
   Literal
   ...

MemberExpression :
   PrimaryExpression
   MemberExpression [ Expression ]
   ...

Yana 0[0]bu kurallara uygun olup, bu kabul edilir geçerli ifade. Doğru olup olmadığı (örneğin, çalışma zamanında bir hata atmıyor) başka bir hikaye, ama evet öyle. JS aşağıdaki gibi ifadeleri bu şekilde değerlendirir someLiteral[someExpression]:

  1. değerlendirmek someExpression(keyfi karmaşık olabilir)
  2. değişmezi karşılık gelen bir nesne türüne dönüştür (sayısal değişmezler => Number, dizeler => Stringvb.)
  3. get propertysonuç (2) ' deki işlemi sonuç (1) özellik adı ile çağırın
  4. sonucu sil (2)

Yani 0[0]şu şekilde yorumlanır

index = 0
temp = Number(0)
result = getproperty(temp, index) // it's undefined, but JS doesn't care
delete temp
return result

Aşağıda , geçerli ancak yanlış bir ifade örneği verilmiştir :

null[0]

İyi ayrıştırıldı, ancak çalışma zamanında yorumlayıcı 2. adımda başarısız oluyor (çünkü nullbir nesneye dönüştürülemiyor) ve bir çalışma zamanı hatası atıyor.


1
Bundan daha fazlası var. var x = null; var a = x[0];bir sözdizimi hatası oluşturmaz, ancak çalışma zamanında bir TypeError atar.
jfriend00

@ jfriend00: Sorunun tamamı bu değildi, ama eklendi.
georg

sonucun tanımsız olması gerekmez. 0[0]Tanımsız değer yerine bir değer döndürmek mümkündür
Rune FS

9

Javascript'te bir sayıyı geçerli bir şekilde belirtebileceğiniz durumlar vardır:

-> 0['toString']
function toString() { [native code] }

Bunu neden yapmak isteyeceğiniz hemen anlaşılmasa da, Javascript'te abone olmak noktalı gösterim kullanmaya eşdeğerdir (her ne kadar nokta gösterimi sizi tanımlayıcıları anahtar olarak kullanmakla sınırlandırsa da).


@AmitJoki Bu, (0).toString(işlevi çağırmadan) ile aynıdır . Sayı türünün bir özelliğidir.
user4642212

@AmitJoki, 'bu satır neden geçerli' sorusunu yanıtladığı için.
Duncan

@Duncan ama bu daha çok "parantez notasyonu nedir" ve sanırım OP bunu biliyor. O Numara nesnesi olarak yorumlanır ve daha sonra onun gerçeği 0mülkiyet erişilir ve o yok çünkü undefinedolduğunu daha jfriend00 açıklandığı gibi doğru.
Amit Joki

@AmitJoki, 0[0]tanımsız olarak dönecek olan yanlış bir varsayım . Olması muhtemeldir, ancak böyle olmak zorunda değildir
Rune FS

9

Bu geçerli sözdiziminin hiçbir şekilde Javascript'e özgü olmadığını belirtmek isterim . Çoğu dilde bir çalışma zamanı hatası veya bir tür hatası olur, ancak bu bir sözdizimi hatasıyla aynı şey değildir. Javascript, verilen adın bir özelliğine sahip olmayan bir nesneye abone olunması da dahil olmak üzere, başka bir dilin bir istisna oluşturabileceği birçok durumda tanımsız döndürmeyi seçer.

Sözdizimi, bir ifadenin türünü bilmez (sayısal bir değişmez gibi basit bir ifade bile) ve herhangi bir işleci herhangi bir ifadeye uygulamanıza izin verir. Örneğin, Javascript'te bir alt simge oluşturma undefinedveya buna nullneden olma TypeError. Bu bir sözdizimi hatası değildir - eğer bu asla yürütülmezse (bir if ifadesinin yanlış tarafında olmak), herhangi bir soruna neden olmaz, oysa bir sözdizimi hatası tanım gereği her zaman derleme zamanında yakalanır (eval, Function, vb. hepsi derleme olarak sayılır).


8

Çünkü bu geçerli bir sözdizimi ve hatta yorumlanacak geçerli bir kod. Herhangi bir nesnenin herhangi bir özelliğine erişmeyi deneyebilirsiniz (ve bu durumda 0, bir Sayı nesnesine dönüştürülecektir) ve eğer varsa, aksi takdirde tanımlanmamışsa size değeri verecektir. Ancak, tanımlanmamış özelliğine erişmeye çalışmak işe yaramaz, bu nedenle 0 [0] [0] bir çalışma zamanı hatasıyla sonuçlanır. Bu yine de geçerli sözdizimi olarak sınıflandırılır. Neyin geçerli sözdizimi olduğu ve neyin çalışma zamanı / derleme zamanı hatalarına neden olmayacağı arasında bir fark vardır.


3

Sadece sözdizimi geçerli undefinedolmakla kalmaz, sonucun çoğu durumda olması gerekmez, tüm mantıklı durumlarda olmasa da. JS, en saf nesne yönelimli dillerden biridir. OO olarak adlandırılan dillerin çoğu, bir kez oluşturulduktan sonra nesnenin şeklini (sınıfa bağlı) değiştiremeyeceğiniz, yalnızca nesnenin durumunu değiştiremeyeceğiniz anlamında sınıf odaklıdır. JS'de nesnenin durumunu ve şeklini değiştirebilirsiniz ve bunu düşündüğünüzden daha sık yaparsınız. Bu yetenek, yanlış kullanırsanız, oldukça belirsiz bir kod oluşturur. Rakamlar değişmezdir, bu yüzden nesnenin kendisini değiştiremezsiniz, durumunu veya formunu değiştiremezsiniz, böylece yapabilirsiniz

0[0] = 1;

Bu, 1 döndüren ancak gerçekte hiçbir şey atamayan geçerli bir atama ifadesidir 0, Sayı değişmezdir. Hangi kendi içinde biraz tuhaf. Hiçbir şey atamayan (*) geçerli ve doğru (yürütülebilir) bir değerlendirme ifadesine sahip olabilirsiniz. Bununla birlikte, sayı türü değiştirilebilir bir nesnedir, bu nedenle türü değiştirebilirsiniz ve değişiklikler prototip zincirinde kademeli olarak azalacaktır.

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]); 

Tabii ki bu, mantıklı kullanım kategorisinden çok uzak ama dil buna izin verecek şekilde belirlenmiş çünkü diğer senaryolarda, nesnelerin yeteneklerini genişletmek aslında çok mantıklı. Bir örnek vermek için jQuery eklentileri jQuery nesnesine bu şekilde bağlanır.

(*) Aslında 1 değerini bir nesnenin özelliğine atar, ancak bu (transcient) nesneye atıfta bulunmanın hiçbir yolu yoktur ve bu nedenle nexx GC geçişinde toplanır


3

JavaScript'te her şey nesnedir, bu nedenle yorumlayıcı onu ayrıştırdığında, 0'ı bir nesne olarak görür ve 0'ı bir özellik olarak döndürmeye çalışır. Doğru veya "" (boş dizge) 0'ıncı öğesine erişmeye çalıştığınızda da aynı şey olur.

0 [0] = 1 ayarlasanız bile, özelliği ve değerini bellekte ayarlayacaktır, ancak 0'a eriştiğinizde bir sayı olarak davranır (Burada Nesne ve sayı olarak muamele görmek arasında kafa karıştırmayın.)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.