1) Unikać tworzenia zmiennych bez słowa kluczowego var.
Każde wywołanie typu:zmienna = "tekst";
function Nic() {
zmienna = "tekst";
}
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:
var x = y = 0;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:
var x = 1;
function Nic() {
alert(x);//tu będzie wartość undefined !!!
var x = 2;
alert(x);
}
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:
for( var i = 0, tab_max = tablica.length; i < tab_max; i++) { }
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).
var person = {
name: "jan",
age : 21
};
for( var p in person) {
if (person.hasOwnProperty(p)) {
alert(p + " " + person[p]);
}
}
5) Niepotrzebne funkcje
Często spotykaną praktyką jest tworzenie pomocniczych funkcji wewnątrz funkcji konstruujących obiekty. Np:
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');
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.
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');
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:
var Company = {};
Company.Module1 = {};
....
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:
var Company = Company || {};
Company.Module1 = Company.Module1 || {};
....
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:
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');
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ł.
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';
}
}
})();
8) Tworzenie obiektów
Eleganckim sposobem jest stworzenie metody fabrykującej obiekty danego typu. Np:
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');