Neden JavaScript'te 2 == [2]?


164

Son zamanlarda bunu 2 == [2]JavaScript'te keşfettim . Anlaşıldığı gibi, bu tuhaflığın birkaç ilginç sonucu var:

var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true

Benzer şekilde, aşağıdakiler çalışır:

var a = { "abc" : 1 };
a[["abc"]] === a["abc"]; // this is also true

Yine de yabancı olsa bile, bu da işe yarar:

[[[[[[[2]]]]]]] == 2; // this is true too! WTF?

Bu davranışlar tüm tarayıcılarda tutarlı görünmektedir.

Bunun neden bir dil özelliği olduğu hakkında bir fikriniz var mı?

İşte bu "özellik" in daha çılgın sonuçları:

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

var a = [0];
a == a // true
a == !a // also true, WTF?

Bu örnekler jimbojw http://jimbojw.com şöhretinin yanı sıra walkingeyerobot tarafından bulundu .

Yanıtlar:


134

Karşılaştırma algoritmasını ECMA-spesifikasyonunda arayabilirsiniz (probleminiz için ECMA-262'nin ilgili bölümleri, 3. baskı: 11.9.3, 9.1, 8.6.2.6).

İlgili soyut algoritmaları tekrar JS'ye çevirirseniz, değerlendirirken neler olur 2 == [2]:

2 === Number([2].valueOf().toString())

burada valueOf()diziler için dizinin kendisini döndürür ve tek öğeli dizinin dize temsili, tek öğenin dize temsilidir.

Bu aynı zamanda üçüncü örneği [[[[[[[2]]]]]]].toString()hala sadece dize gibi açıklar 2.

Gördüğünüz gibi, sahne arkasında oldukça fazla sihir var, bu yüzden genellikle sadece katı eşitlik operatörünü kullanıyorum ===.

Özellik adları her zaman dize olduğundan birinci ve ikinci örneği takip etmek daha kolaydır.

a[[2]]

eşittir

a[[2].toString()]

hangisi sadece

a["2"]

Herhangi bir dizi büyüsü gerçekleşmeden önce sayısal anahtarların bile özellik adları (dizeler) olarak ele alındığını unutmayın.


10

Bunun nedeni, ==operatörün örtük tür dönüşümüdür .

[2], bir Sayı ile karşılaştırıldığında Sayı 2'ye dönüştürülür. Tekli +operatörü [2] 'de deneyin .

> +[2]
2

Diğerleri [2] 'nin bir dizgeye dönüştürüldüğünü söylüyorlar. +"2"aynı zamanda 2 sayısıdır.
dlamblin

1
Aslında, o kadar kolay değil. [2] dizgiye daha yakın olacak ama ecma-international.org/ecma-262/5.1/#sec-11.9.3
neo

10
var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true

Denklemin sağ tarafında, 2 değerine sahip bir sayı türü döndüren a [2] 'ye sahibiz. Solda, önce tek bir 2 nesnesiyle yeni bir dizi oluşturuyoruz. Sonra bir [( dizi burada)). Bu bir dize veya bir sayı için değerlendirilir emin değilim. 2 veya "2". Önce dize durumunu ele alalım. Ben bir ["2"] yeni bir değişken oluşturmak ve null döneceğini düşünüyorum. null! == 2. Öyleyse, aslında dolaylı olarak bir sayıya dönüştüğünü varsayalım. a [2], 2 (2 ve 2) tür (yani === çalışır) ve değerle eşleşir. Ben bir [değer] bir dize veya sayı beklediğinden dolaylı olarak bir dizi dizi dönüştürmek düşünüyorum. Sayı daha yüksek önceliğe sahip gibi görünüyor.

Bir yan notta, bu önceliği kimin belirlediğini merak ediyorum. Çünkü [2] ilk öğe olarak bir sayıya sahiptir, bu yüzden bir sayıya dönüşür mü? Ya da bir diziyi bir [diziye] geçirirken diziyi önce bir sayıya, sonra dizeye dönüştürmeye çalışır. Kim bilir?

var a = { "abc" : 1 };
a[["abc"]] === a["abc"];

Bu örnekte, abc adlı bir üyesi olan a adlı bir nesne oluşturuyorsunuz. Denklemin sağ tarafı oldukça basit; a.abc'ye eşdeğerdir. Bu 1 değerini döndürür. Sol taraf ilk önce ["abc"] bir gerçek dizisi oluşturur. Daha sonra, yeni oluşturulan diziyi ileterek nesne üzerinde bir değişken ararsınız. Bu bir dize beklediğinden, dizeyi bir dizeye dönüştürür. Bu şimdi 1'e eşit olan bir ["abc"] olarak değerlendirilir. 1 ve 1 aynı türdür (bu yüzden === çalışır) ve eşit değerdir.

[[[[[[[2]]]]]]] == 2; 

Bu sadece örtük bir dönüşümdür. === bu durumda işe yaramaz çünkü bir tür uyuşmazlığı vardır.


Öncelikle ilgili sorunuzun cevabı: sırayla yöntemini çağıran dizi için ==geçerlidir , bu yüzden aslında karşılaştıracağınız dize numarasıdır ; dize ve sayı arasındaki karşılaştırma dize dönüştürülerek yapılırToPrimitive()toString()2"2"
Christoph

8

İçin ==neden durumda, bu Doug Crockford hep kullanılmasını önerir ===. Örtülü tür dönüşümü yapmaz.

İle olan örneklerde ===, örtük tip dönüşümü eşitlik operatörü çağrılmadan önce yapılır.


7
[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

Bu ilginç, bu [0] hem doğru hem de yanlış değil, aslında

[0] == true // false

JavaScript () operatörünün komik işleme yöntemidir.


4
aslında, komik bir şekilde ==çalışıyor; gerçek bir açık döküm (yani Boolean([0])veya !![0]) [0]kullanırsanız true, boole bağlamlarında olması gerektiği gibi değerlendireceğinizi göreceksiniz : JS'de, herhangi bir nesne kabul edilirtrue
Christoph

6

Bir öğeden oluşan bir dizi, öğenin kendisi olarak değerlendirilebilir.

Bu ördek yazmasından kaynaklanmaktadır. "2" den beri == 2 == [2] ve muhtemelen daha fazlası.


4
çünkü tipte uyuşmuyorlar. ilk örnekte, ilk önce sol taraf değerlendirilir ve bunlar eşleşen tipte sarılır.
Shawn

8
Ayrıca, ördek yazmanın burada doğru kelime olduğunu düşünmüyorum. ==Karşılaştırmadan önce operatör tarafından gerçekleştirilen örtük tip dönüşümüyle ilgilidir .
Chetan S

14
Bunun ördek yazmayla ilgisi yoktur, daha ziyade zayıf yazmayla, yani örtük tip dönüşümü ile ilgilidir
Christoph

@Chetan: ne dedi;)
Christoph

2
Chetan ve Christoph ne dedi.
Tim Down

3

Bir karşılaştırırken diğer cevaplar için biraz detay eklemek için ... Arraya Number, JavaScript dönüştürür Arrayile parseFloat(array). Farklı Arraydeğerlerin dönüştürülmesini görmek için konsolda (örneğin Firebug veya Web Inspector) kendiniz deneyebilirsiniz .

parseFloat([2]); // 2
parseFloat([2, 3]); // 2
parseFloat(['', 2]); // NaN

İçin Arrays, parseFloatişlemi gerçekleştiren Arrayilk üyesi ve ıskarta dinlenme.

Düzenleme: Christoph ayrıntılarına göre, daha uzun formu dahili olarak kullanıyor olabilir, ancak sonuçları sürekli olarak aynı parseFloat, böylece her zaman parseFloat(array)nasıl dönüştürüleceğini bilmek için stenografi kullanabilirsiniz .


2

Her durumda 2 nesneyi karşılaştırıyorsunuz .. Kullanmayın ==, karşılaştırmayı düşünüyorsanız, === aklınızdadır, == değil. == genellikle çılgınca etkiler verebilir. Dildeki iyi kısımları arayın :)


0

Sorunun EDIT bölümü için açıklama :

1. Örnek

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

Christoph'un yukarıdaki cevabı uyarınca ilkel bir değere ilk daktilo [0] "0" ( [0].valueOf().toString())

"0" == false

Şimdi Boolean (false) öğesini Number olarak ve sonra String ("0") değerini Number olarak yazın.

Number("0") == Number(false)
or  0 == 0 
so, [0] == false  // true

Gelince ifaçıklamada, eğer durumda açık bir karşılaştırma kendisi için şart değerlendirir yoksa truthy değerler.

Yalnızca 6 yanlış değer vardır : false, null, undefined, 0, NaN ve boş dize "". Ve sahte bir değer olmayan her şey gerçek bir değerdir.

[0] bir sahte değer olmadığından, gerçek bir değerdir, ififade doğru olarak değerlendirir ve ifadeyi yürütür.


2. Örnek

var a = [0];
a == a // true
a == !a // also true, WTF?

İlkel değerleri tekrar dökümü yazın,

    a = a
or  [0].valueOf().toString() == [0].valueOf().toString()
or  "0" == "0" // true; same type, same value


a == !a
or  [0].valueOf().toString() == [0].valueOf().toString()
or  "0" == !"0"
or  "0" == false
or  Number("0") == Number(false)
or  0 = 0   // true
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.