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ść thislogowałaby się jako window. Jest tak, ponieważ windowjest to zmienna globalna w zakresie przeglądarki internetowej.
Jeśli uruchomisz ten sam fragment kodu w środowisku takim jak node.js, thisodsył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, thisnie 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. thisw 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 forEachi slice, powinieneś już wiedzieć, że thiszmienna w tym przypadku odnosi się do Objectwywołania tej funkcji (zwróć uwagę, że w javascript prawie wszystko jest Object, w tym Arrays 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 Objectzawiera właściwość, która zawiera a Function, właściwość nazywa się metodą. Wywołana metoda zawsze będzie miała thiszmienną ustawioną na tę, z Objectktó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 thisnie 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 newkluczowe
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 newsłowa kluczowego.
- Wywołanie funkcji ze
newsłowem kluczowym natychmiast zainicjuje Objecttyp Person.
- Konstruktor tego
Objectma ustawiony konstruktor Person. Pamiętaj też, że typeof awalwróci Objecttylko.
- Ten nowy
Objectmiałby przypisany prototyp Person.prototype. Oznacza to, że każda metoda lub właściwość w Personprototypie będzie dostępna dla wszystkich instancji Person, w tym awal.
PersonWywoływana jest teraz sama funkcja ; thisbę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ą constructorfunkcjami 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: calliapply
Więc tak, skoro functions 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ą calli apply, i obie mogą być używane do manipulowania wartością thisfunkcji, 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 thisfunkcję foojako odniesienie thisArg. Wszystkie pozostałe parametry przekazywane callsą przekazywane do funkcji foojako 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 thisdowolnej funkcji.
applyjest prawie taki sam, jak callzaakceptować, że przyjmuje tylko dwa parametry: thisArgi tablicę zawierającą argumenty, które należy przekazać do funkcji. Tak więc powyższe callwezwanie można przetłumaczyć applytak:
foo.apply(thisArg, [1,2,3])
Zauważ, że calli applymoże zastąpić wartość thiswywołania metody set metodą kropkową, którą omówiliśmy w drugim punkcie. Wystarczająco proste :)
Prezentowanie .... bind!
bindjest bratem calli apply. Jest to również metoda odziedziczona przez wszystkie funkcje z globalnego Functionkonstruktora w Javascript. Różnica między bindi call/ applyjest taka, że oba calli applyfaktycznie wywołują funkcję. bindZ drugiej strony, zwraca nową funkcję z thisArgi argumentspre-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 calli apply, bindró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. calli applyzwracał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 thiszmienia 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ść thiszmieniła się wraz z zagnieżdżonym zasięgiem, ale chcieliśmy wartości thisz pierwotnego zakresu. Więc „skopiowane” thisdo thati użyty zamiast kopiować this. Sprytnie, co?
Indeks:
- Co jest
thisdomyś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
thispomocą calli apply?
- Korzystanie
bind.
- Kopiowanie w
thiscelu rozwiązania problemów związanych z zagnieżdżeniem.