this
służy do tworzenia i odwoływania się do poublicznych pól obiektu. Możesz nie uzywać this i tworzyć w konstruktorze zwykłe zmienne, jednak będą one dostępne tylko w obrębie konstruktora, będą prywatne dla konstruktora, niewidoczne nawet dla metod prototypu:
function Example() {
var a = 'foo'
this.b = 'bar'
}
Example.prototype.showA = function() {
return a
}
Example.prototype.showB = function() {
return this.b
};
const instance = new Example()
console.log('Prywatna zmienna konstruktora:', instance.a) // => undefined
console.log('Publiczne pole instancji:', instance.b) // => "bar"
console.log('showB', instance.showB()) // => "bar"
console.log('showA', instance.showA()) // => ReferenceError: a is not defined
W ES5 nie ma możliwości stworzenia za pomocą funkcji konstruktora pól prywatnych widocznych w obrębie całego obiektu (także w metodach prototypu), w ES6 można uzyć do tego symboli (choć to też nie jest pełna prywatność). Rozwiązaniem jest wrzucenie wszystkich metod obiektu do konstruktora (lub przynajmniej tych korzystających z prywatnych zmiennych konstruktora):
function Example() {
var a = 'foo'
this.b = 'bar'
this.showA = function() {
return a
}
this.showB = function() {
return this.b
};
}
const instance = new Example()
console.log('Prywatna zmienna konstruktora:', instance.a) // => undefined
console.log('Publiczne pole instancji:', instance.b) // => "bar"
console.log('showB', instance.showB()) // => "bar"
console.log('showA', instance.showA()) // => "foo"
Ma to tą wadę, że zajmuje więcej pamięci, lecz w wiekszości przypadków nie ma to żadnego znaczenia. No ale jak już tak robimy to nie jest nam potrzebny konstruktor i tworzenie obiektów za pomocą new
, możemy użyć funkcji-fabryki, czyli po prostu funkcji, która zwaca literał obiektowy:
function example() {
const a = 'foo'
return {
b: 'bar',
showA() {
return a
},
showB() {
return this.b
}
}
}
const instance = example()
console.log('Prywatna zmienna konstruktora:', instance.a) // => undefined
console.log('Publiczne pole instancji:', instance.b) // => "bar"
console.log('showB', instance.showB()) // => "bar"
console.log('showA', instance.showA()) // => "foo"
Mało tego - możemy teraz całkowicie wyeliminować this
, które jest wrażliwe na kontekst wywołania i przez to nie działa zawsze tak jak się intuicyjnie wydaje:
function example() {
const a = 'foo'
const pub = {
b: 'bar',
showA() {
return a
},
showB() {
return pub.b
}
}
return pub
}
const instance = example()
console.log('Prywatna zmienna konstruktora:', instance.a) // => undefined
console.log('Publiczne pole instancji:', instance.b) // => "bar"
console.log('showB', instance.showB()) // => "bar"
console.log('showA', instance.showA()) // => "foo"
Polecam ostatnie dwa sposoby - dzięki domknięciom możesz łatwo sterować zasięgiem zmiennych, bardzo łatwo robi sie kompozycję takich obiektów, jest mniej magii.
W ES6 są jeszcze klasy i wspomniane symbole - to podejście też ma zalety, jednak magii tam sporo węc ja preferuję fabryki.
A tak ogólnie to programowanie funkcyjne > programowanie obiektowe ;)
PS:
Jeszcze dodam - jeśli metodę zadeklarujesz bez this
w funkcji-konstruktorze to tak samo jak dla zmiennej a
nie będziesz jej mógł użyć z poziomu instancji (tylko wewnątrz funkcji-konstruktora).