W javascript this
ma tzw. późne wiązanie i zależy od tego jak wywołasz swoją funkcję i skąd. W zasadzie możesz każdą funkcję traktować jako zwykły osobny byt, bo this nie jest automatycznie bindowane tak jak w innych językach.
var foo = {
fn: function() {
console.log("Funckja foo", this);
}
}
foo.fn();
var nieFoo = foo.fn;
nieFoo();
// Funckja foo Object {} wywołana na rzecz obiektu foo
// Funckja foo Window "pożyczona" funkcja już nie ma this pokazującego na foo!
Równie dobrze powyższy kod możemy przedstawić w ten sposób:
var someFunction = function() {
console.log("Funckja foo", this);
};
var foo = {
fn: someFunction
}
foo.fn();
var nieFoo = foo.fn;
nieFoo();
// Funckja foo Object {} wywołana na rzecz obiektu foo
// Funckja foo Window "pożyczona" funkcja już nie ma this pokazującego na foo!
Jak widać otrzymamy to samo.
Ogólnie wygląda to tak:
1. Czy funkcja jest wywołana z operatorem new (new binding)? Jeżeli tak, to this to jest nowo utworzony obiekt.
var bar = new foo();
**2. **Czy funkcja jest wywołana z użyciem call/apply (explicit binding)? Jeżeli tak, to this wewnątrz tej funkcji to będzie ten obiekt czyli obj2.
var bar = foo.call( obj2 )
**3. **Czy funkcja jest wywołana na rzecz jakiegoś obiektu (implicit binding)? Jeżeli tak, to this będzie tym obiektem.
var bar = obj1.foo()
**4. **W każdym innym przypadku zachodzi default binding, czyli this jest obiektem globalnym (chyba, że jest to strict mode to wtedy undefined).
var bar = foo()
W twoim przypadku podczas wywołania metody private zachodzi warunek 4. This nie działa kaskadowo.
var foo = {
bar: function() {
var baz = function () {
console.log("Baz ", this);
};
console.log("Bar ", this);
baz();
}
};
foo.bar();
// Bar Object {} ponieważ zachodzi zasada 3, wywołujemy bar na rzecz obiektu foo
// Baz Window ponieważ zachodzi zasada 4.
Wracając do twojego przykładu, this
w metodzie prywatnej zawsze wskazuje na window
przez zagnieżdzenie. Jeżeli chcemy, żeby to zadziałało musimy wyraźnie zaznaczyć poprzez użycie bind
, że chcemy aby this
pokazywało na F
.
function F()
{
var private = function()
{
return this;
}.bind(this); // wymuszamy, żeby this wewnątrz pokazywał na this, czyli F
this.public1 = function()
{
if (private() === window) //true
console.log("private() === window");
}
this.public2 = function()
{
return this;
}
}
var f = new F();
f.public1();
// undefined
f.public2();
// F {}
Jeżeli chodzi o to:
var f = {};
f.public1 = function...
f.public2 = function...
Użycie słowa new
na funckji w javascript powoduje 4 rzeczy:
- Tworzy nowy obiekt (taki zwykły, pusty
var f = {};
).
- Ustawia prototype na prototyp funkcji konstruktora.
- Wywołuje funkcję konstruktora i używa nowego obiektu z pkt. 1 jako
this
- Zwraca nowo utworzony obiekt. Może to być obiekt, który funkcja sama stworzy, ale możemy również zwrócić nasz własny!
function F() {
var API = {
sayHi: function() {
console.log("Hello");
}
}
return API;
}
function Fn() {
this.sayHi = function() {
console.log("Hello");
}
}
var f = new F();
var fn = new Fn();
f.sayHi();
fn.sayHi();
Więc tak, z tego co wiem jest to identyczne z wywołaniem funkcji konstruktora
Warto dodać, że js nie implementuje kontroli dostępu, ale możemy ją uzyskać korzystając z Closure, czyli w skrócie z tego, że każda funkcja ma dodatkowo dostęp do "środowiska" ją otaczającego.
Do dalszego poczytania:
https://github.com/getify/You-Dont-Know-JS