Javascript's this
Proste wywoływanie funkcji
Rozważ następującą funkcję:
function foo() {
console.log("bar");
console.log(this);
}
foo(); // calling the function
Zauważ, że działamy w trybie normalnym, tzn. Tryb ścisły nie jest używany.
Podczas pracy w przeglądarce wartość this
logowałaby się jako window
. Jest tak, ponieważ window
jest to zmienna globalna w zakresie przeglądarki internetowej.
Jeśli uruchomisz ten sam fragment kodu w środowisku takim jak node.js, this
odsyłam do zmiennej globalnej w Twojej aplikacji.
Teraz, jeśli uruchomimy to w trybie ścisłym, dodając instrukcję "use strict";
na początku deklaracji funkcji, this
nie będzie już odwoływał się do zmiennej globalnej w żadnym ze środowisk. Odbywa się to w celu uniknięcia pomyłek w trybie ścisłym. this
w tym przypadku po prostu loguje się undefined
, ponieważ tak właśnie jest, nie jest zdefiniowane.
W poniższych przypadkach zobaczymy, jak manipulować wartością this
.
Wywołanie funkcji na obiekcie
Można to zrobić na różne sposoby. Jeśli wywołałeś metody rodzime w Javascripcie, takie jak forEach
i slice
, powinieneś już wiedzieć, że this
zmienna w tym przypadku odnosi się do Object
wywołania tej funkcji (zwróć uwagę, że w javascript prawie wszystko jest Object
, w tym Array
s iFunction
s). Weźmy na przykład następujący kod.
var myObj = {key: "Obj"};
myObj.logThis = function () {
// I am a method
console.log(this);
}
myObj.logThis(); // myObj is logged
Jeśli an Object
zawiera właściwość, która zawiera a Function
, właściwość nazywa się metodą. Wywołana metoda zawsze będzie miała this
zmienną ustawioną na tę, z Object
którą jest skojarzona. Dotyczy to zarówno trybów ścisłych, jak i nie ścisłych.
Zauważ, że jeśli metoda jest przechowywana (a raczej kopiowana) w innej zmiennej, odwołanie do this
nie jest już zachowywane w nowej zmiennej. Na przykład:
// continuing with the previous code snippet
var myVar = myObj.logThis;
myVar();
// logs either of window/global/undefined based on mode of operation
Biorąc pod uwagę bardziej praktyczny scenariusz:
var el = document.getElementById('idOfEl');
el.addEventListener('click', function() { console.log(this) });
// the function called by addEventListener contains this as the reference to the element
// so clicking on our element would log that element itself
Słowo new
kluczowe
Rozważ funkcję konstruktora w JavaScript:
function Person (name) {
this.name = name;
this.sayHello = function () {
console.log ("Hello", this);
}
}
var awal = new Person("Awal");
awal.sayHello();
// In `awal.sayHello`, `this` contains the reference to the variable `awal`
Jak to działa? Zobaczmy, co się stanie, gdy użyjemy new
słowa kluczowego.
- Wywołanie funkcji ze
new
słowem kluczowym natychmiast zainicjuje Object
typ Person
.
- Konstruktor tego
Object
ma ustawiony konstruktor Person
. Pamiętaj też, że typeof awal
wróci Object
tylko.
- Ten nowy
Object
miałby przypisany prototyp Person.prototype
. Oznacza to, że każda metoda lub właściwość w Person
prototypie będzie dostępna dla wszystkich instancji Person
, w tym awal
.
Person
Wywoływana jest teraz sama funkcja ; this
będący odniesieniem do nowo zbudowanego obiektu awal
.
Całkiem proste, co?
Zauważ, że oficjalna specyfikacja ECMAScript nigdzie nie stwierdza, że tego typu funkcje są constructor
funkcjami rzeczywistymi . Są to po prostu normalne funkcje inew
można ich używać w dowolnej funkcji. Po prostu używamy ich jako takich, dlatego nazywamy je tylko takimi.
Wywoływanie funkcji w funkcjach: call
iapply
Więc tak, skoro function
s też sąObjects
(i faktycznie zmiennymi pierwszej klasy w Javascript), nawet funkcje mają metody, które są ... no cóż, same w sobie.
Wszystkie funkcje dziedziczą po globalnym Function
, a dwie z jego wielu metod są call
i apply
, i obie mogą być używane do manipulowania wartością this
funkcji, na której są wywoływane.
function foo () { console.log (this, arguments); }
var thisArg = {myObj: "is cool"};
foo.call(thisArg, 1, 2, 3);
Jest to typowy przykład użycia call
. Zasadniczo przyjmuje pierwszy parametr i ustawia this
funkcję foo
jako odniesienie thisArg
. Wszystkie pozostałe parametry przekazywane call
są przekazywane do funkcji foo
jako argumenty.
Tak więc powyższy kod zaloguje się {myObj: "is cool"}, [1, 2, 3]
do konsoli. Całkiem niezły sposób na zmianę wartości this
dowolnej funkcji.
apply
jest prawie taki sam, jak call
zaakceptować, że przyjmuje tylko dwa parametry: thisArg
i tablicę zawierającą argumenty, które należy przekazać do funkcji. Tak więc powyższe call
wezwanie można przetłumaczyć apply
tak:
foo.apply(thisArg, [1,2,3])
Zauważ, że call
i apply
może zastąpić wartość this
wywołania metody set metodą kropkową, którą omówiliśmy w drugim punkcie. Wystarczająco proste :)
Prezentowanie .... bind
!
bind
jest bratem call
i apply
. Jest to również metoda odziedziczona przez wszystkie funkcje z globalnego Function
konstruktora w Javascript. Różnica między bind
i call
/ apply
jest taka, że oba call
i apply
faktycznie wywołują funkcję. bind
Z drugiej strony, zwraca nową funkcję z thisArg
i arguments
pre-set. Weźmy przykład, aby lepiej to zrozumieć:
function foo (a, b) {
console.log (this, arguments);
}
var thisArg = {myObj: "even more cool now"};
var bound = foo.bind(thisArg, 1, 2);
console.log (typeof bound); // logs `function`
console.log (bound);
/* logs `function () { native code }` */
bound(); // calling the function returned by `.bind`
// logs `{myObj: "even more cool now"}, [1, 2]`
Widzisz różnicę między tymi trzema? Jest subtelny, ale używa się go inaczej. Podobnie jak call
i apply
, bind
również przewyższy wartośćthis
ustawioną przez wywołanie metodą kropkową.
Zauważ też, że żadna z tych trzech funkcji nie zmienia żadnej z pierwotnych funkcji. call
i apply
zwracałby wartość ze świeżo skonstruowanych funkcji whilebind
zwróci samą świeżo skonstruowaną funkcję, gotową do wywołania.
Dodatkowe rzeczy, skopiuj to
Czasami nie podoba ci się fakt, że this
zmienia się wraz z zasięgiem, zwłaszcza zasięgiem zagnieżdżonym. Spójrz na następujący przykład.
var myObj = {
hello: function () {
return "world"
},
myMethod: function () {
// copy this, variable names are case-sensitive
var that = this;
// callbacks ftw \o/
foo.bar("args", function () {
// I want to call `hello` here
this.hello(); // error
// but `this` references to `foo` damn!
// oh wait we have a backup \o/
that.hello(); // "world"
});
}
};
W powyższym kodzie widzimy, że wartość this
zmieniła się wraz z zagnieżdżonym zasięgiem, ale chcieliśmy wartości this
z pierwotnego zakresu. Więc „skopiowane” this
do that
i użyty zamiast kopiować this
. Sprytnie, co?
Indeks:
- Co jest
this
domyślnie przechowywane ?
- Co jeśli wywołamy funkcję jako metodę z notacją Object-dot?
- Co jeśli użyjemy
new
słowa kluczowego?
- Jak manipulujemy za
this
pomocą call
i apply
?
- Korzystanie
bind
.
- Kopiowanie w
this
celu rozwiązania problemów związanych z zagnieżdżeniem.