2009-11-12 13 views
160

Niedawno odkryłem, że 2 == [2] w JavaScript. Jak się okazuje, to dziwactwo ma kilka ciekawych konsekwencji:Dlaczego 2 == [2] w JavaScript?

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

Podobnie następujących prac:

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

Jeszcze dziwniejsze, to działa tak samo:

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

Te zachowania wydają się być spójne we wszystkich przeglądarkach.

Każdy pomysł, dlaczego jest to funkcja językowa?

Oto bardziej szalone konsekwencje tej „funkcji”:

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

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

Przykłady te zostały znalezione przez jimbojw http://jimbojw.com sławy, jak walkingeyerobot.

Odpowiedz

126

Możesz sprawdzić algorytm porównywania w specyfikacji ECMA (odpowiednie sekcje ECMA-262, wydanie trzecie dla twojego problemu: 11.9.3, 9.1, 8.6.2.6).

Jeśli przetłumaczyć zaangażowanych abstrakcyjnych algorytmów powrotem do JS, co się dzieje, gdy oceny 2 == [2] jest w zasadzie tak:

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

gdzie valueOf() dla tablic zwraca samą tablicę i string-reprezentację tablicy jednoelementowa jest reprezentacją ciągów pojedynczego elementu.

To wyjaśnia również trzeci przykład jako [[[[[[[2]]]]]]].toString(), który wciąż jest ciągiem znaków 2.

Jak widać, w grę wchodzi sporo magii zza kulis, dlatego zazwyczaj używam tylko operatora ścisłej równości ===.

Pierwszym i Drugim przykładem są łatwiejsze do naśladowania jako nazwy właściwości zawsze są struny, więc

a[[2]] 

jest równoważna

a[[2].toString()] 

który jest po prostu

a["2"] 

Należy pamiętaj, że nawet klawisze numeryczne są traktowane jako nazwy właściwości (tj. łańcuchy), zanim zdarzy się jakakolwiek magia tablicowa.

6

Tablica jednego przedmiotu może być traktowana jako przedmiot.

Wynika to z pisania na maszynie. Od "2" == 2 == [2] i prawdopodobnie więcej.

+8

2 === [2] nie działa. –

+0

Hmm wydaje się tak, mój zły. –

+4

, ponieważ nie pasują do typu. w pierwszym przykładzie lewa strona jest oceniana jako pierwsza i kończy się dopasowaniem w typie. – Shawn

10

Wynika to z niejawnej konwersji typu operatora ==.

[2] jest konwertowany na Liczba to 2 w porównaniu z liczbą. Wypróbuj jednoargumentowy operator + w [2].

> +[2] 
2 
+0

Inni mówią, że [2] jest konwertowany na ciąg znaków. '+" 2 "' jest również liczbą 2. – dlamblin

+1

W rzeczywistości nie jest to takie proste. [2] jest konwertowane na ciąg, byłoby bliżej, ale spójrz na http://ecma-international.org/ecma-262/5.1/#sec-11.9.3 – neo

8

Dla przypadku ==, dlatego Doug Crockford zaleca zawsze przy użyciu ===. Nie wykonuje żadnej niejawnej konwersji typów.

Dla przykładów z ===, niejawna konwersja typu jest wykonywana przed wywołaniem operatora równości.

+1

używa === w swoim przykładzie. –

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

Po prawej stronie równania, mamy do [2], który zwraca typu numer o wartości 2. Po lewej stronie, to najpierw tworząc nową tablicę z jednego przedmiotu 2. Następnie wywołujemy [(tablica jest tutaj)]. Nie jestem pewien, czy to jest wartość ciągu lub liczby. 2 lub "2". Zacznijmy od przypadku łańcucha. Wierzę, że ["2"] utworzy nową zmienną i zwróci wartość null. null! == 2. Przyjmijmy więc, że jest to pośrednia konwersja na liczbę. a [2] zwróci 2. 2 i 2 dopasowanie typu (czyli === works) i wartość. Myślę, że niejawnie konwertuje tablicę na liczbę, ponieważ [wartość] oczekuje ciągu lub liczby. Wygląda na to, że liczba ma wyższy priorytet.

Na marginesie, zastanawiam się, kto określa to pierwszeństwo. Czy dlatego, że [2] ma numer jako pierwszy element, więc konwertuje na liczbę? A może po przejściu tablicy do [tablicy] spróbuje najpierw przekształcić tablicę w liczbę, a potem w łańcuch. Kto wie?

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

W tym przykładzie tworzysz obiekt o nazwie a z elementem o nazwie abc. Prawa strona równania jest całkiem prosta; jest to odpowiednik a.abc. To zwraca 1. Lewa strona najpierw tworzy literalną tablicę ["abc"]. Następnie wyszukuje się zmienną na obiekcie, przekazując nowo utworzoną tablicę. Ponieważ oczekuje ciąg znaków, konwertuje tablicę na ciąg znaków.Teraz jest to wartość ["abc"], która wynosi 1. 1 i 1 są tego samego typu (dlatego === działa) i mają taką samą wartość.

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

To tylko niejawna konwersja. === nie działałoby w tej sytuacji, ponieważ występuje niedopasowanie typu.

+0

Odpowiedź na pytanie o pierwszeństwo: '==' stosuje 'ToPrimitive()' do tablicy, która z kolei wywołuje jego metodę 'toString()', więc to, co faktycznie porównujesz, to liczba '2' do ciągu znaków' ' "2" '; porównanie ciągu znaków i liczby odbywa się poprzez konwersję ciągu – Christoph

3

Aby dodać trochę szczegółów do innych odpowiedzi ... podczas porównywania Array do Number, JavaScript konwertuje Array z parseFloat(array). Możesz wypróbować to samemu w konsoli (np. Firebug lub Web Inspector), aby przekonwertować wartości na różne wartości Array.

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

Na Array s parseFloat wykonuje operacje na pierwszym elemencie Array „S i usuwa resztę.

Edytuj: Szczegóły Per Christoph, może to oznaczać, że używa on dłuższego formularza wewnętrznie, ale wyniki są identyczne z parseFloat, więc zawsze możesz użyć skrótu parseFloat(array), aby dowiedzieć się, jak zostanie przekonwertowany.

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

To ciekawe, nie jest to, że [0] jest zarówno prawdziwe i fałszywe, faktycznie

[0] == true // false 

jest obsługa JavaScript w zabawny sposób przetwarzania jeśli operator().

+4

w rzeczywistości, jest to zabawny sposób '==' działa; jeśli użyjesz rzeczywistego rzutowania jawnego (tj. 'Boolean ([0])' lub '!! [0]'), przekonasz się, że '[0]' przetworzy wartość 'true' w kontekstach logicznych, tak jak powinno: w JS dowolny obiekt jest uważany za "prawdziwy" – Christoph

1

Porównywujesz 2 obiekty w każdym przypadku. Nie używaj ==, jeśli myślisz o porównaniu, masz na myśli ===, a nie ==. == często daje szalone efekty.Szukać dobrych części w języku :)

0

wyjaśnienie EDIT części pytania:

1 przykładu

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

Pierwszy typecast [0] do prymitywnego wartości jako na odpowiedź Christopha powyżej mamy "0" ([0].valueOf().toString())

"0" == false 

Teraz typecast Boolean (false) na numer, a następnie String („0”) w Number

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

chodzi o if oświadczenie, jeśli nie ma wyraźnego porównania w jeśli warunek sobie, warunek dla wartości truey.

Istnieje tylko 6 wartości falsy: false, null, undefined, 0, NaN i pusty ciąg "". Wszystko, co nie jest wartością falsyfikacyjną, jest wartością prawdy.

Ponieważ [0] nie jest wartością falsyfikacyjną, jest wartością prawdy, if zwraca wartość true & wykonuje instrukcję.


2 przykład

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

Ponownie typu odlewania wartości prymitywny

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 
Powiązane problemy