Dziwne elementy JavaScript

wtf

Poziom średnio-zaawansowany

Niniejszy artykuł to mała odskocznia. Mianowicie przejrzymy dziwne elementy JavaScript. Niektóre są po prostu zabawne, inne powodują że debug kodu trwa nieco dłużej. Dobrze jest mieć świadomość tych osobliwych zachowań i warto sobie o nich przypomnieć, gdy nasz kod zacznie wyprawiać dziwne rzeczy.

Dziwne elementy JavaScript

Oczywiście wiele zależy od samej przeglądarki (implementacji JavaScript’u). W nowszych wersjach danej przeglądarki niektóre osobliwości mogą być usunięte / obsłużone w jakiś konkretny sposób.

NaN to jednak Number

Not a Number (NaN) is a Number czyli witamy w JavaScript.

alert(typeof NaN); // 'Number'

alert(NaN === NaN); // false

Aby potwierdzić czy coś jest liczbą czy nie, użyjmy funkcji isNaN().

Null jest obiektem

alert(typeof null); // 'object'

„No to co, w JavaScript wszystko jest obiektem…”. Tylko że:

alert(null instanceof Object); // false

🙂

Funkcje mogą wywołać się same

Funkcje zwykle zawierają kod do wykonania w określonym czasie (wywołanie funkcji). Nie ma jednak blokady dla kodu w postaci:

(function() { alert('hello'); })(); // 'hello'

Funkcja od razu zostanie wywołana i wykonana.

Czy 0.1 + 0.2 równa się 0.3 ?

Właściwie tak, ale w JavaScript wynosi konkretnie 0.30000000000000004.

Chodzi o precyzję obliczeń zmiennoprzecinkowych. JavaScript implementuje to z drobną osobliwością, która jednak może przysporzyć kłopotów programiście operującemu (szczególnie porównującemu wartości) na takich liczbach.

Tak jak w poniższym kodzie, który niestety zwróci false:

var num1 = 0.1, num2 = 0.2, cmp = 0.3;
alert(num1 + num2 == cmp); // false

Można zastosować na przykład takie obejście:

alert(num1 + num2 > cmp - 0.001 && num1 + num2 < cmp + 0.001); 
// true

OK, kto zgłębiał zagadnienia implementacji obsługi liczb zmiennoprzecinkowych wie, że to naprawdę skomplikowany temat.

Równe nie zawsze jest równe

Przykładowo:

1 == 1; // true 
'foo' == 'foo'; // true 
[1,2,3] == [1,2,3]; // false

Porównanie tablic zwraca false. To dlatego, że tak jak w wielu innych językach, operuje się na referencji do tablicy, a nie dokładnie na wartości.

Ale jeśli porównamy:

new Array(3) == ",,"; // true

otrzymamy true. To z kolei dlatego, że łańcuchowa reprezentacja takiej tablicy to „,,”:

new Array(3).toString(); //",,"

JavaScript konwertuje wyrażenia po obu stronach na string podczas użycia operatora porównania ==.

Dlatego nie należy zapominać o operatorze === (porównanie nie tylko wartości ale i typu) przy konstruowaniu porównań:

new Array(3) === ",,"; // false

„Arytmetyka” obiektów i tablic

Sadyści lubią znęcać się nad językiem JavaScript właśnie poprzez używanie operatorów typowych do operowania na liczbach, dla obiektów i tablic.

alert(1 * 'foo'); // NaN
alert(1 * 2); // 2
alert('foo' * 1); // NaN
alert('foo' + 1); // 'foo1'

Sprawa jasna – dodając 1 do łańcucha otrzymamy połączenie dwóch łańcuchów (tutaj: foo1). Nie ma natomiast mnożenia na łańcuchach, zatem słusznie otrzymujemy NaN.

Tablica plus obiekt daje obiekt:

[] + {}; // [object Object]

ale dlaczego taka operacja daje liczbę 1 w wyniku?

alert( + ! + []); // 1

dziwactwo? Dodajmy jeszcze funkcje i pomyślmy jak JavaScript wyczarował łańcuch „42” w wyniku takiej operacji:

[]+(-~function(){}-~function(){}-~function(){}-~function(){}) + (-~function(){}-~function(){})

🙂

Dodatnie i ujemne zero?

To może wynikać z samego standardu IEEE 754, z którego pochodzi takie cudo jak zero ze znakiem.

alert( (+0) + " " + (-0) ); // wynik 0 0

Jasne – konwersja na łańcuch. Zatem zobaczmy jak JavaScript porównuje ze sobą „dodatnie” i „ujemne” zero:

	
alert((+0 === -0)); // true

alert((-0 < +0)); // false

Natomiast jeśli podzielimy przez 0, otrzymamy plus nieskończoność:

alert((1 / 0)); // Infinity

ale dzieląc przez „minus” zero, otrzymamy minus nieskończoność:

alert((1 / -0)); // -Infinity

Użycie instanceof

Operator ten sprawdza, czy obiekt jest instancją danego typu (nie mylimy z typeof). I w tym przypadku operacje mogą dać niespodziewany wynik.

Na przykład łańcuch powinien być instancją typu String:

alert("hello" instanceof String); // false
alert(new String("hello") instanceof String); // true

A jednak należy użyć konstruktora do stworzenia łańcucha, aby operator zadziałał.

Więcej? Polecam dedykowaną stronę http://wtfjs.com/ 🙂

Podsumowanie

Nie jest tajemnicą że JavaScript posiada wiele osobliwości, które oddalają go od doskonałości. Ale nie przeszkadza to temu językowi królować w programowaniu frontend’u a nawet wchodzić w nowe obszary zastosowań. Używając tego języka intensywnie, można chyba do tych dziwnych elementów i zachowań przywyknąć.

Na pocieszenie, artykuły takie jak ten:
https://wiki.theory.org/YourLanguageSucks#JavaScript_sucks_because

pokazują że nie tylko JavaScript, ale i inne języki raczą nas dziwactwami.

JavaScript i tak przeszedł już długą drogę i za pewne jeszcze wiele przed nim – tak jak i przed programistami.

Programista WWW i aplikacji mobilnych z wieloletnim doświadczeniem, początkujący bloger. Pasjonat programowania, nowych technologii, e-commerce, a także sportu i motoryzacji.

Twitter LinkedIn Google+ Skype Xing 

Podaj dalej: Share on Facebook1Tweet about this on TwitterShare on Google+1Share on LinkedIn0Share on Tumblr0Digg thisEmail this to someonePin on Pinterest1
Możesz skomentować leave a response, lub podać trackback z własnej strony.