Tym razem kilka wskazówek dotyczących kodu w JS.

1) Unikać tworzenia zmiennych bez słowa kluczowego var.

Każde wywołanie typu: {code brush: js} zmienna = "tekst"; {/code} {code brush: js} function Nic() { zmienna = "tekst"; } {/code} powoduje utworzenie zmiennej globalnej. Zmienna ta staje się właściwością obiektu globalnego. W przeglądarkach internetowych jest to obiekt window. Prowadzi to do zaśmiecania obiektu globalnego przypadkowymi właściwościami, w rezultacie czego może dojść do nadpisywania wartości i nieoczekiwanych zachowań w działaniu aplikacji.

Nie należy też korzystać z konstrukcji typu:

{code brush: js} var x = y = 0; {/code} Efektem takiego zapisu jest stworzenie zmiennej globalnej y.

2) Deklaracja zmiennych na początku

Najbezpieczniej jest zadeklarować zmienne lokalne na początku funkcji. Jest to czytelne i nie doprowadza do niepotrzebnych pomyłek, np: {code brush: js} var x = 1; function Nic() { alert(x);//tu będzie wartość undefined !!! var x = 2; alert(x); } {/code}

3) Optymalizacja w pętlach for

Przy poruszaniu się po elementach tablicy z wykorzystaniem pętli for, optymalnie jest wyliczyć max liczbę elementów RAZ, czyli: {code brush: js} for( var i = 0, tab_max = tablica.length; i < tab_max; i++) { } {/code}
Uwaga! Ma to szczególne znaczenie przy poruszaniu się po elementach struktury DOM. Są to bowiem kosztowne operacje.

4) Uwaga na pętle for-in

Przy iteracji po właściwościach obiektu najbezpieczniej jest dodać warunek odfiltrowujący właściwości dodane przez prototypy (jeśli chcemy iterować tylko te własne). {code brush: js} var person = { name: "jan", age : 21 }; for( var p in person) { if (person.hasOwnProperty(p)) { alert(p + " " + person[p]); } } {/code}

5) Niepotrzebne funkcje

Często spotykaną praktyką jest tworzenie pomocniczych funkcji wewnątrz funkcji konstruujących obiekty. Np: {code brush: js} var Car = function(mark, model) { this.mark = mark; this.model = model; this.carName = function () { return this.mark+ " " + this.model; } } var car1 = new Car('ford', 'focus'); var car2 = new Car('ford', 'mondeo'); {/code} Powoduje to tworzenie dodatkowej funkcji dla każdego obiektu Car. W tym jednak przypadku (jak i w wielu innych podobnych) można ją zastąpić funkcją dodaną przez prototyp. {code brush: js} var Car = function(mark, model) { this.mark = mark; this.model = model; } Car.prototype.carName = function() { return this.mark+ " " + this.model; }; var car1 = new Car('ford', 'focus'); var car2 = new Car('ford', 'mondeo'); {/code}

6) Przestrzeń nazw

Złożoność kodu JS rośnie bardzo szybko, a wraz z nim pojawia się potrzeba podzielenia go na moduły. Bardzo często spotykaną praktyką jest tworzenie "przestrzeni nazw". Przeważnie robi się to tak:

{code brush: js} var Company = {}; Company.Module1 = {}; .... {/code}

Jednak kiedy w kilku plikach będziemy mieć taką konstrukcję to drugi będzie nadpisywać utworznone wcześniej zmienne w pierwszym pliku. Można to łatwo obejść posługując się operatorem OR:

{code brush: js} var Company = Company || {}; Company.Module1 = Company.Module1 || {}; .... {/code}

Dzięki temu jeśli zmienna np. Company nie będzie dostępna operator || zwróci pusty obiekt (zainicjujemy więc tylko wtedy gdy "Company" nie będzie istnieć).

Można pójść o krok dalej i stworzyć pomocniczą metodę, która będzie tworzyć przestrzenie nazw, co jest najlepszym rozwiązaniem:

{code brush: js} var Company = Company || {}; Company.namespace = function(namespace_str) { var segments = namespace_str.split('.'); var part = Company; // nazwa firmy jest bazową przestrzenią która znajduje się już w zmiennej "part" if (segments[0] === 'Company') { segments = segments.slice(1); } // tworzenie kolejnych segmentów przestrzeni nazw o ile nie istnieją for(var i = 0, max = segments.length; i < max; i += 1) { if (typeof(part[segments[i]]) === 'undefined') { part[segments[i]] = {}; } part = part[segments[i]]; } return part; }; // przykłady użycia Company.namespace('Company.Module1'); Company.namespace('Module2'); Company.namespace('Company.Module2.Submodule1'); {/code}

7) Moduły

Bardzo przydatny w praktyce jest wzorzec modułu. W połączeniu z przestrzeniami nazw zapewnia nam podział kodu na logiczne moduły oraz hermetyzację. Konstrukcja wykorzystuje funkcję natychmiastową, która od razu inicjuje nasz moduł.

{code brush: js} Company.Module1.Submodule1 = (function(){ // odwolania do innych modulow var mod2 = Company.Module2.Obj; // zmienne prywatne var x,y,z; // metody prywatne var func1 = function(param1) { x = param1; }; // zwracany obiekt (metody publiczne) return { func1 : func1, func2 : function(param1){ return mod2.call(param1) === 'test'; } } })(); {/code}

8) Tworzenie obiektów

Eleganckim sposobem jest stworzenie metody fabrykującej obiekty danego typu. Np:

{code brush:js} Company.Factory = new function() { function Car(name) { var cName = name; this.getName = function () { return cName; } } this.makeCar = function(name) { return new Car(name); } } // przyklad uzycia var car = Company.Factory.makeCar('ford'); {/code}