Zapewne wiele razy w swoim kodzie korzystałeś z pętli for
, zazwyczaj wygląda ona tak:
for (var i = 0; i < array.length; i++) {
// array[i]
}
Prawdopodobnie jest to najczęściej stosowana konstrukcja, ale warto pamiętać o tym, iż nie jedyna. Prede wszystkim zwróćmy uwagę na array.length
, do którego odwołujemy się przy kolejnych iteracjach odczytując za każdym razem tę samą wartość. Skoro tak, to możemy lekko zmodyfikować naszą pętlę zapisując wielkość tablicy w zmiennej:
for (var i = 0, len = array.length; i < len; i++) {
// array[i]
}
Jeśli potrzebujemy natomiast przeiterować tablicę od końca zamiast od początku, to możemy skorzystać z poniższej konstrukcji:
for (var i = array.length-1; i >= 0; i--) {
// array[i]
}
Oczywiście są to tylko najprostsze przykłady - możemy dowolnie modyfikować pętle for
dostosowując je do naszych potrzeb.
Poprawną metodą dodawania zdarzeń zgodnie ze standardami jest funkcja addEventListener
. Przykładowe użycie wygląda tak:
var element = document.getElementById("some");
element.addEventListener("click", function() {
console.log("foobar");
}, false);
Dziedziczenie w JavaScript można uzyskać na kilka sposobów. Jedną z dostępnych metod jest użycie Object.create
(nowa konstrukcja z ECMAScript 5), gdzie pierwszy argument to obiekt, na podstawie którego ma zostać utworzony nowy. Zaawansowani programiści z łatwością dostrzegą, że pierwszym argumentem funkcji Object.create
jest tak naprawdę prototyp, który służy jako baza do utworzenia nowego obiektu.
var human = {
name: "",
gender: null,
getName: function() { return this.name; }
};
var girl = Object.create(human);
girl.gender = "female";
girl.name = "Veronica";
console.log(girl.getName()); // Veronica
W celu usunięcia elementu o danym indeksie w tablicy, używamy funkcji splice
:
var arr = [1, 2, 3];
arr.splice(0, 1);
console.log(arr); // [2, 3]
Pierwszy argument oznacza numer indeksu, od którego chcemy zacząć operację usuwania, a drugi liczbę elementów, które chcemy usunąć. W powyższym przykładzie chcemy pozbyć się jednego elementu, poczynając od indeksu 0.
Warto dodać, że funkcja ta zwraca tablicę elementów, które usunęliśmy:
var arr = [3, 4, 6];
console.log(arr.splice(2, 1)); // [6]
Obiekt Function w JavaScript posiada własność length, która zwraca ilość oczekiwanych przez daną funkcję argumentów. Aby dowiedzieć się, ile argumentów zostało funkcji przekazanych, możemy użyć lokalnej dla danej funkcji zmiennej arguments:
var a = function(one, two, three, four) {
return arguments.length;
};
a.length; // 4
a(1,2,3); // 3
Aby uzyskać czas unixowy w milisekundach, mierzony od 1 stycznia 1970 00:00:00 UTC, używamy:
new Date().getTime();
lub gdy nie zależy nam na starszych przeglądarkach:
Date.now();
Istnieje również skrócona, jednak mniej czytelna wersja:
+new Date;
Gdy chcemy dodać zawartość tablicy do końca drugiej, jest na to dość prosty sposób:
var a = ["raz", "dwa"];
var b = ["trzy", "cztery"];
Array.prototype.push.apply(a,b);
Wartości z tablicy b zostaną dodane do a:
console.log(a); // ["raz", "dwa", "trzy", "cztery"]
Drugim sposobem, jeżeli nie chcemy modyfikować oryginalnych tablic lub mamy do połączenia więcej niż dwie tablice, jest concat:
var c = ["pięć", "sześć"];
var d = a.concat(b, c);
console.log(d); // ["raz", "dwa", "trzy", "cztery", "pięć", "sześć"]
Często w wyniku operacji arytmetycznych otrzymujemy wynik o wartości NaN
. Skrót pochodzi od wyrażenia not a number. Wartość tę możemy dostać na przykład poprzez podzielenie liczby przez stringa:
var i = 10;
var result = i / "foobar";
console.log(result); // NaN
Jak więc sprawdzić, czy rezultalt to NaN, by uniknąć dalszych błędów w aplikacji? Można użyć funkcji isNaN
:
isNaN(result); // true
Jest to problematyczne, o ile weźmiemy pod uwagę, że sprawdzana wartość będzie czymś innym niż NaN
:
isNaN({}); // true
Można temu zaradzić definiując własną funkcję do sprawdzenia, czy zmienna to NaN
. Otóż warto zauważyć, że NaN
jako jedyna wartość nie daje prawdy przy porównaniu do samej siebie:
result === result; // false
Na tej podstawie konstruujemy własną funkcję sprawdzającą, alternatywną do isNaN
:
var customIsNaN = function(i) {
return i !== i;
}
Załóżmy, że dołączasz skrypty JavaScript w tradycyjny sposób, poprzez umieszczenie elementu <script> w <head>:
<head>
<script src="main.js"></script>
Ta metoda ma jednak wady - opóźnia ona ładowanie stron w starszych przeglądarkach - browser czeka, dopóki załaduje się plik JavaScript, dopiero potem zaczyna renderować wszystko, co występuje po tagu <script>.
Jakie są alternatywy? Można użyć parametru async z HTML5, który pominie kolejność ładowania skryptów i załaduje je asynchronicznie:
<head>
<script src="main.js" async="true"></script>
Warto też wiedzieć, że aby uniknąć oczekiwania i nie polegać na parametrze async, można również dołączyć swoje skrypty dopiero w <body> na samym końcu dokumentu HTML. Mamy wtedy pewność, że DOM jest załadowany i gotowy do użycia (a więc zapomnieć można o zdarzeniu DOMContentLoaded).
Więcej w temacie na MDN.
Czasami musimy wygenerować losową liczbę całkowitą z podanego zakresu. Niestety dostępna funkcja Math.random();
generuje liczby z przedziału 0-1. Własna implementacja funkcji rand może wyglądać tak:
function rand( min, max ){
min = parseInt( min, 10 );
max = parseInt( max, 10 );
if ( min > max ){
var tmp = min;
min = max;
max = tmp;
}
return Math.floor( Math.random() * ( max - min + 1 ) + min );
}
Kolejność parametrów w zasadzie nie ma znaczenia.
W innych językach programowania przeważnie porównujemy ze sobą elementy za pomocą podwójnego znaku równości '=='. Jednak ten operator nie uwzględnia typu porównywanych obiektów (JS próbuje przekształcić obiekty na wspólny typ), z czego może wyniknąć wiele nieoczekiwanych błedów w kodzie.
0 == "0" // => true
![] == [] // => true
undefined == null // => true
Dobrą praktyką jest używanie potrójnego znaku równości, który uwzględnia także typ porównywanych elementów. Pozwala to na wyeliminowanie z kodu wielu niejasności.
0 === "0" // => false
![] === [] // => false
undefined === null // => false
Dzięki automatycznemu wywoływaniu anonimowych funkcji możesz łatwo wprowadzić wewnętrzny scope zmiennych, dzięki czemu będą one dostępne tylko w tej funkcji.
(function() {
var privateVar = 1;
})();
console.log(privateVar); // undefined
Największą zaletą frameworka jQuery był łatwy sposób pobierania elementów drzewa DOM według selektorów CSS. Na przykład:
var news = $("div.news");
Ładowanie całego jQuery tylko dla takiej funkcjonalności jest często przerostem formy nad treścią. Z łatwością możesz skorzystać z document.querySelectorAll:
var news = document.querySelectorAll("div.news"); // wszystkie elementy div.news
var news = document.querySelector("div.news"); // tylko pierwszy element
Sprawdź wsparcie dla querySelector wśród współczesnych przeglądarek na canIUse.com.
Sprawdzenie, czy zmienna jest tablicą było w JavaScript nie do końca prostym zadaniem. Niestety, typeof nie daje nam jednoznacznej odpowiedzi, ponieważ zwraca to samo dla tablic i obiektów:
var arr = [1, 2, 3];
var obj = {};
typeof arr; // "object"
typeof obj; // "object"
W ECMAScript 3 sprawdzaliśmy, czy zmienna jest tablicą następująco:
var arr = [1, 2, 3];
if (Object.prototype.toString.call(arr) === "[object Array]") {}
Na szczęście najnowsza wersja ECMAScript 5 wprowadza funkcję Array.isArray:
var arr = [1, 2, 3];
if (Array.isArray(arr)) {}
Załóżmy, że dysponujesz gotową tablicą i chcesz usunąć jej wszystkie elementy. Z reguły robi się to tak:
var arr = [1, 2, 3];
// ...
arr = [];
Jest to dobry pomysł, ale jeśli zależy nam na optymalizacji nawet tak trywialnego przypadku (sprawdza się to w kontekście developmentu gier, gdzie najlepiej zużywać jak najmniej pamięci) możemy skorzystać z właściwości length:
var arr = [1, 2, 3];
arr.length = 0;
console.log(arr); // []
W ten sposób korzystamy zawsze z tej samej struktury danych i przy okazji unikamy tworzenia nowej tablicy.
Przyjmijmy, że mamy do czynienia z następującą tablicą:
var arr = [1, 2, 3];
Jak znaleźć jej największą wartość? Zapewne do głowy przychodzi Ci pętla i dodatkowa zmienna przechowująca aktualnie największy element. W JavaScript można to zrobić kompleksowo, przy pomocy Math.max:
var arr = [1, 2, 3];
Math.max.apply(null, arr); // 3
Podobnie da się zrobić z najmniejszą wartością, wykorzystując Math.min:
var arr = [1, 2, 3];
Math.min.apply(null, arr); // 1
Pamiętaj, że parseInt traktuje stringa jako liczbę w systemie ósemkowym, jeśli na jego początku pojawi się "0". Dobrą praktyką jest umieszczanie zawsze "10" jako drugiego argumentu, który mówi, że powinniśmy sparsować liczbę w systemie dziesiętnym.
parseInt("08"); // 0
parseInt("08", 10); // 8
Pamiętaj, że pętle for in stworzone są tylko i wyłącznie do iteracji po obiektach:
var obj = { key : 1 };
for (var i in obj) {
console.log(i); // "key"
}
For in używany z tablicami będzie iterował również przez własności .prototype tablicy, co jest efektem niepożądanym.
Zapomnij o tworzeniu tablic i obiektów w, wydawałoby się, klasyczny sposób:
var arr = new Array();
var obj = new Object();
W JavaScript robi się to następująco:
var arr = [];
var obj = {};
JavaScript umożliwia tworzenie tych struktur natywnie, nie musimy dodatkowo wołać ich konstruktora. Poza tym można je nadpisać, co może przysporzyć dużo problemów:
var Object = null;
Bardzo popularnym rozwiązaniem pozwalającym sprawdzać poprawność kodu pod względem dobrych praktyk jest JSLint autorstwa Douglasa Crockforda.
Alternatywnym projektem dla JSLint jest... JSHint.
Oba projekty można użyć w systemach continuous integration, które mogą np. sprawdzać po każdym commicie, czy kod jest poprawny.