Konstruktory w JavaScript również wyglądają inaczej niż innych językach. Każde
wywołanie funkcji, które jest poprzedone słowem kluczowym new, zachowuje się
jak konstruktor.
Wewnątrz konstruktora - wywoływanej fukcji - wartość this wskazuje na
nowo utworzony obiekt Object. Prototyp prototype tego
nowego obiektu będzie wskazywał na prototyp prototype obiektu fukcji,
która została wywołana jako konstruktor.
Jeżeli wywołana funkcja nie posiada jawnej deklaracji return, wówczas
fukcja domyślnie zwraca wartość this - nowy obiekt.
function Foo() {
this.bla = 1;
}
Foo.prototype.test = function() {
console.log(this.bla);
};
var test = new Foo();
Powyżej wywołanya została funkcja Foo jako konstruktor oraz ustawia
nowo utworzonemu obiektowi właściwość prototype na Foo.prototype.
W tym przypadku jawna deklaracja return w funkcji zwraca wartość
ustawioną w deklaracji, ale tylko jeżeli zwracaną wartością jest
obiekt Object.
function Bar() {
return 2;
}
new Bar(); // nowy obiekt
function Test() {
this.value = 2;
return {
foo: 1
};
}
new Test(); // zwrócony obiekt
Jeżeli słowo kluczowe new zostanie pominięte, funkcja nie zwróci nowego
obiektu.
function Foo() {
this.bla = 1; // zostanie ustawiona w obiekcie global
}
Foo(); // undefined
Mimo że powyższy kod może zadziałać w pewnych przypadkach, w związku
z działaniem this w języku JavaScript, to jako
wartość thiszostanie wykorzystany obiekt global.
Aby móc ominąć słowo kluczowe new, konstruktor musi jawnie zwracać wartość.
function Bar() {
var value = 1;
return {
method: function() {
return value;
}
}
}
Bar.prototype = {
foo: function() {}
};
new Bar();
Bar();
Oba wywołania Bar zwrócą tę samą rzecz, nowo utworzony obiekt, który posiada
właściwość nazwaną method i dla którego Bar jest Domknięciem.
Należy również pamiętać, że wywołanie new Bar() nie ma wpływu na
prototyp zwróconego obiektu (prototypem będzie object.prototype a nie Bar.prototype).
Kiedy prototyp zostanie przypisany do nowo utworzonego obiektu, Bar nidgy
nie zwróci tego nowego obiektu Bar, tylko literał obiektu, który jest po
słowie kluczowym return.
W powyższym przykładzie nie ma żadnej różnicy w działaniu pomiędzy użyciem
i nieużyciem słowa kluczowego new.
Często zaleca się nie korzystać z operatora new, ponieważ zapominanie
o jego stosowaniu może prowadzić do błędów.
W celu stworzenia nowego obiektu, powinno się używać fabryki i konstruować nowy obiekt wewnątrz tej fabryki.
function Foo() {
var obj = {};
obj.value = 'blub';
var private = 2;
obj.someMethod = function(value) {
this.value = value;
}
obj.getPrivate = function() {
return private;
}
return obj;
}
Mimo że powyższy kod jest odporny na brak słowa kluczowego new i ułatwia
korzystanie ze zmiennych prywatnych, to posiada
pewne wady.
While the above is robust against a missing new keyword and certainly makes
the use of private variables easier, it comes with some
downsides.
- Zużywa więcej pamięci, ponieważ tworzony obiekt nie współdzieli metod poprzez prototyp.
- Aby móc dziedziczyć fabryka musi skopiować wszystkie metody z dziedziczonego obiektu lub przypisać ten obiekt, z którego się dziedziczy, jako prototyp do nowo utworzonego obiektu.
- Porzucenie łańcucha prototypów tylko ze względu na opuszczone słowo kluczowe
newjest sprzeczne z duchem języka.
Pominięcie słowa kluczowego new może prowadzić do błędów, ale na pewno nie
powinno to być powodem odrzucenia używania prototypów w ogóle. Sprowadza się to
do wyboru rozwiązania, które bardziej pasuje do potrzeb aplikacji. Szczególnie
ważne jest, aby wybrać określony styl tworzenia obiektów i trzymać się go.