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

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++) {  }
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).
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');