Jak działa słowo kluczowe „to”?


1308

Zauważyłem, że wydaje się, że nie ma jasnego wyjaśnienia, czym thisjest słowo kluczowe i jak jest poprawnie (i niepoprawnie) używane w JavaScript w witrynie przepełnienia stosu.

Byłem świadkiem tego bardzo dziwnego zachowania i nie rozumiem, dlaczego tak się stało.

Jak thisdziała i kiedy należy go używać?


6
Znalazłem to, gdy przejrzałem
Wai Wong


2
Peter Michaux this odradza
Marcel Korpel

1
Omówienie MDN nie jest wcale takie złe ... developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/…
dat

2
Ciekawe wyjaśnienie thissłowa kluczowego: rainsoft.io/gentle-explanation-of-this-in-javascript
Dmitri Pavlutin

Odpowiedzi:


1350

Najpierw polecam przeczytanie artykułu Mike'a Westa Scope w JavaScript ( mirror ). Jest to doskonałe, przyjazne wprowadzenie do pojęć thisi łańcuchów zasięgu w JavaScript.

Kiedy zaczniesz się przyzwyczajać this, zasady są dość proste. Standard ECMAScript 5.1 definiuje this:

§11.1.1this kluczowe

Do thisocenia słowo kluczowe aby wartości ThisBinding bieżącego kontekstu wykonania

To powiązanie jest czymś, co interpreter JavaScript utrzymuje podczas oceny kodu JavaScript, podobnie jak specjalny rejestr procesora, który zawiera odniesienie do obiektu. Tłumacz interpretuje ThisBinding za każdym razem, gdy ustanawia kontekst wykonania w jednym z trzech różnych przypadków:

1. Wstępny globalny kontekst wykonania

Jest tak w przypadku kodu JavaScript, który jest oceniany na najwyższym poziomie, np. Gdy jest bezpośrednio w <script>:

<script>
  alert("I'm evaluated in the initial global execution context!");

  setTimeout(function () {
      alert("I'm NOT evaluated in the initial global execution context.");
  }, 1);
</script>

Podczas oceny kodu w początkowym globalnym kontekście wykonania ThisBinding jest ustawiany na obiekt globalny window( § 10.4.1.1 ).

Wprowadzanie kodu ewaluacyjnego

  • … Przez bezpośrednie wywołanie eval() ThisBinding pozostaje niezmienione; jest to ta sama wartość, co ThisBinding kontekstu wykonania wywołania ( § 10.4.2 (2) (a)).

  • … Jeśli nie przez bezpośrednie wywołanie eval()
    ThisBinding jest ustawiane na obiekt globalny, tak jakby było wykonywane w początkowym globalnym kontekście wykonania ( § 10.4.2 (1)).

§15.1.2.1.1 określa, do czego jest bezpośrednie połączenie eval(). Zasadniczo eval(...)jest to połączenie bezpośrednie, podczas gdy coś takiego (0, eval)(...)lub var indirectEval = eval; indirectEval(...);połączenie pośrednie eval(). Zobacz odpowiedź chuckj w celu (1), eval ( 'To') vs eval ( 'To') w JavaScript? oraz szczegółowo ECMA-262-5 Dmitrija Soshnikova. Rozdział 2. Tryb ścisły. na kiedy możesz użyć połączenia pośredniego eval().

Wprowadzanie kodu funkcji

Dzieje się tak podczas wywoływania funkcji. Jeśli funkcja zostanie wywołana na obiekcie, takim jak in obj.myMethod()lub ekwiwalent obj["myMethod"](), wówczas ThisBinding zostanie ustawione na obiekt ( objw przykładzie; § 13.2.1 ). W większości innych przypadków ThisBinding jest ustawiony na obiekt globalny ( § 10.4.3 ).

Powodem pisania „w większości innych przypadków” jest to, że istnieje osiem wbudowanych funkcji ECMAScript 5, które umożliwiają określenie ThisBinding na liście argumentów. Te specjalne funkcje przyjmują tzw. thisArgThisBinding podczas wywoływania funkcji ( § 10.4.3 ).

Te specjalne wbudowane funkcje to:

  • Function.prototype.apply( thisArg, argArray )
  • Function.prototype.call( thisArg [ , arg1 [ , arg2, ... ] ] )
  • Function.prototype.bind( thisArg [ , arg1 [ , arg2, ... ] ] )
  • Array.prototype.every( callbackfn [ , thisArg ] )
  • Array.prototype.some( callbackfn [ , thisArg ] )
  • Array.prototype.forEach( callbackfn [ , thisArg ] )
  • Array.prototype.map( callbackfn [ , thisArg ] )
  • Array.prototype.filter( callbackfn [ , thisArg ] )

W przypadku Function.prototypefunkcji są one wywoływane na obiekcie funkcji, ale zamiast ustawienia ThisBinding na obiekt funkcji, ThisBinding jest ustawiane na thisArg.

W przypadku Array.prototypefunkcji podana callbackfnjest wywoływana w kontekście wykonania, w którym ThisBinding jest ustawione, thisArgjeśli jest dostarczone; w przeciwnym razie do obiektu globalnego.

Takie są reguły zwykłego JavaScript. Kiedy zaczynasz korzystać z bibliotek JavaScript (np. JQuery), może się okazać, że niektóre funkcje bibliotek manipulują wartością this. Twórcy tych bibliotek JavaScript robią to, ponieważ zazwyczaj obsługuje najczęściej spotykane przypadki użycia, a użytkownicy biblioteki zazwyczaj uważają to zachowanie za wygodniejsze. Przekazując funkcje zwrotne odnoszące się thisdo funkcji bibliotecznych, należy zapoznać się z dokumentacją, aby uzyskać wszelkie gwarancje dotyczące wartości thiswywołanej funkcji.

Jeśli zastanawiasz się, w jaki sposób biblioteka JavaScript manipuluje wartością this, biblioteka po prostu używa jednej z wbudowanych funkcji JavaScript akceptujących thisArg. Ty także możesz napisać własną funkcję, korzystając z funkcji zwrotnej i thisArg:

function doWork(callbackfn, thisArg) {
    //...
    if (callbackfn != null) callbackfn.call(thisArg);
}

Jest szczególny przypadek, o którym jeszcze nie wspomniałem. Podczas konstruowania nowego obiektu za pośrednictwem newoperatora interpreter JavaScript tworzy nowy, pusty obiekt, ustawia niektóre właściwości wewnętrzne, a następnie wywołuje funkcję konstruktora na nowym obiekcie. Zatem, gdy funkcja jest wywoływana w kontekście konstruktora, wartością thisjest nowy obiekt utworzony przez interpreter:

function MyType() {
    this.someData = "a string";
}

var instance = new MyType();
// Kind of like the following, but there are more steps involved:
// var instance = {};
// MyType.call(instance);

Funkcje strzałek

Funkcje strzałek (wprowadzone w ECMA6) zmieniają zakres this. Zobacz istniejące pytanie kanoniczne, funkcja strzałki vs deklaracja / wyrażenie funkcji: czy są one równoważne / wymienne? po więcej informacji. Ale w skrócie:

Funkcje strzałek nie mają własnych this... powiązań. Zamiast tego identyfikatory te są rozwiązywane w zakresie leksykalnym, jak każda inna zmienna. Oznacza to, że wewnątrz funkcji strzałki this... odwołuje się do wartości thisw środowisku, w którym funkcja strzałki jest zdefiniowana.

Dla zabawy sprawdź swoje zrozumienie na kilku przykładach

Aby wyświetlić odpowiedzi, najedź myszką na jasnoszare pola.

  1. Jaka jest wartość thisw zaznaczonej linii? Dlaczego?

    window - Zaznaczony wiersz jest oceniany w początkowym globalnym kontekście wykonania.

    if (true) {
        // What is `this` here?
    }
  2. Jaka jest wartość thisw zaznaczonej linii, gdy obj.staticFunction()jest wykonywana? Dlaczego?

    obj - Podczas wywoływania funkcji na obiekcie ThisBinding jest ustawiane na obiekt.

    var obj = {
        someData: "a string"
    };
    
    function myFun() {
        return this // What is `this` here?
    }
    
    obj.staticFunction = myFun;
    
    console.log("this is window:", obj.staticFunction() == window);
    console.log("this is obj:", obj.staticFunction() == obj);
      

  3. Jaka jest wartość thisw zaznaczonej linii? Dlaczego?

    window

    W tym przykładzie interpreter JavaScript wprowadza kod funkcji, ale ponieważ myFun/ obj.myMethodnie jest wywoływany na obiekcie, ThisBinding jest ustawione na window.

    Różni się to od Pythona, w którym dostęp do metody ( obj.myMethod) tworzy powiązany obiekt metody .

    var obj = {
        myMethod: function () {
            return this; // What is `this` here?
        }
    };
    var myFun = obj.myMethod;
    console.log("this is window:", myFun() == window);
    console.log("this is obj:", myFun() == obj);
      

  4. Jaka jest wartość thisw zaznaczonej linii? Dlaczego?

    window

    Ten był trudny. Podczas oceny kodu ewaluacji thisjest obj. Jednak w kodzie eval myFunnie jest wywoływany na obiekcie, więc ThisBinding jest ustawione windowna wywołanie.

    function myFun() {
        return this; // What is `this` here?
    }
    var obj = {
        myMethod: function () {
            eval("myFun()");
        }
    };
  5. Jaka jest wartość thisw zaznaczonej linii? Dlaczego?

    obj

    Linia myFun.call(obj);wywołuje specjalną funkcję wbudowaną Function.prototype.call(), która przyjmuje thisArgjako pierwszy argument.

    function myFun() {
        return this; // What is `this` here?
    }
    var obj = {
        someData: "a string"
    };
    console.log("this is window:", myFun.call(obj) == window);
    console.log("this is obj:", myFun.call(obj) == obj);
      


6
@Ali: Są to odniesienia do sekcji w edycji 5.1 standardu ECMAScript, ECMA-262 . Zapewniam je, abyś mógł przeczytać Standard w sprawie szczegółów technicznych, jeśli chcesz.
Daniel Trebbien

1
Myślę, że @supertonsky ma rację co do nr 2 - jeśli myFun () jest wywoływana z zasięgu globalnego, a nie jako metoda na obiekcie, „to” będzie obiektem globalnym, więc sformułowanie pytania ma znaczenie. btw - Naprawdę podoba mi się pomysł najechania kursorem myszy, aby uzyskać odpowiedź na coś takiego.
user655489,

2
Ale jsfiddle.net/H4LYm/2 pokazuje, że setTimeoutprzykład ma thisod window(global).
Kevin Meredith,

2
pochodząc z Pythona można sobie wyobrazić poziom frustracji, kiedy wpadłem na trzeci przykład .. smh
Marius Mucenicu

1
Ta odpowiedź powinna prawdopodobnie zostać zaktualizowana, aby odzwierciedlić rzeczywistość ES2020, nawet jeśli zmiany są tylko terminologiczne.
Ben Aston

156

Że thiszachowuje się różnie w JavaScript słów kluczowych w porównaniu do innych języków. W językach zorientowanych obiektowo thissłowo kluczowe odnosi się do bieżącego wystąpienia klasy. W JavaScript wartość thisjest określona przez kontekst wywołania funkcji ( context.function()) i miejsce jej wywołania.

1. W przypadku użycia w kontekście globalnym

Gdy używasz thisw kontekście globalnym, jest on powiązany z obiektem globalnym ( windoww przeglądarce)

document.write(this);  //[object Window]

Gdy używasz thiswewnątrz funkcji zdefiniowanej w kontekście globalnym, thisnadal jest powiązany z obiektem globalnym, ponieważ funkcja ta jest faktycznie metodą globalnego kontekstu.

function f1()
{
   return this;
}
document.write(f1());  //[object Window]

Powyżej f1jest metoda obiektu globalnego. W ten sposób możemy również wywołać go na windowobiekcie w następujący sposób:

function f()
{
    return this;
}

document.write(window.f()); //[object Window]

2. Przy zastosowaniu metody wewnątrz obiektu

Gdy używasz thissłowa kluczowego w metodzie obiektowej, thisjest on związany z „bezpośrednim” otaczającym obiektem.

var obj = {
    name: "obj",
    f: function () {
        return this + ":" + this.name;
    }
};
document.write(obj.f());  //[object Object]:obj

Powyżej umieściłem słowo natychmiast w podwójnych cudzysłowach. Chodzi o to, aby po umieszczeniu obiektu w innym obiekcie thispowiązać go z bezpośrednim rodzicem.

var obj = {
    name: "obj1",
    nestedobj: {
        name:"nestedobj",
        f: function () {
            return this + ":" + this.name;
        }
    }            
}

document.write(obj.nestedobj.f()); //[object Object]:nestedobj

Nawet jeśli funkcja zostanie jawnie dodana do obiektu jako metoda, nadal będzie on przestrzegać powyższych reguł, co thisnadal wskazuje na bezpośredni obiekt nadrzędny.

var obj1 = {
    name: "obj1",
}

function returnName() {
    return this + ":" + this.name;
}

obj1.f = returnName; //add method to object
document.write(obj1.f()); //[object Object]:obj1

3. Podczas wywoływania funkcji bezkontekstowej

Gdy używasz thisfunkcji wewnętrznej, która jest wywoływana bez kontekstu (tj. Nie na żadnym obiekcie), jest ona powiązana z obiektem globalnym ( windoww przeglądarce) (nawet jeśli funkcja jest zdefiniowana wewnątrz obiektu).

var context = "global";

var obj = {  
    context: "object",
    method: function () {                  
        function f() {
            var context = "function";
            return this + ":" +this.context; 
        };
        return f(); //invoked without context
    }
};

document.write(obj.method()); //[object Window]:global 

Wypróbuj to wszystko z funkcjami

Możemy również wypróbować powyższe punkty za pomocą funkcji. Istnieją jednak pewne różnice.

  • Powyżej dodaliśmy elementy do obiektów za pomocą notacji literalnej obiektu. Możemy dodawać członków do funkcji za pomocą this. określić je.
  • Zapis literalny obiektu tworzy instancję obiektu, z której możemy natychmiast korzystać. W przypadku funkcji konieczne może być najpierw utworzenie jej wystąpienia za pomocą newoperatora.
  • Również w podejściu dosłownym do obiektu możemy jawnie dodawać członków do już zdefiniowanego obiektu za pomocą operatora kropki. Zostanie to dodane tylko do określonej instancji. Jednak dodałem zmienną do prototypu funkcji, aby została odzwierciedlona we wszystkich instancjach funkcji.

Poniżej wypróbowałem wszystkie rzeczy, które zrobiliśmy z Objectem i thiswyżej, ale najpierw stworzyłem funkcję zamiast bezpośrednio pisać obiekt.

/********************************************************************* 
  1. When you add variable to the function using this keyword, it 
     gets added to the function prototype, thus allowing all function 
     instances to have their own copy of the variables added.
*********************************************************************/
function functionDef()
{
    this.name = "ObjDefinition";
    this.getName = function(){                
        return this+":"+this.name;
    }
}        

obj1 = new functionDef();
document.write(obj1.getName() + "<br />"); //[object Object]:ObjDefinition   

/********************************************************************* 
   2. Members explicitly added to the function protorype also behave 
      as above: all function instances have their own copy of the 
      variable added.
*********************************************************************/
functionDef.prototype.version = 1;
functionDef.prototype.getVersion = function(){
    return "v"+this.version; //see how this.version refers to the
                             //version variable added through 
                             //prototype
}
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   3. Illustrating that the function variables added by both above 
      ways have their own copies across function instances
*********************************************************************/
functionDef.prototype.incrementVersion = function(){
    this.version = this.version + 1;
}
var obj2 = new functionDef();
document.write(obj2.getVersion() + "<br />"); //v1

obj2.incrementVersion();      //incrementing version in obj2
                              //does not affect obj1 version

document.write(obj2.getVersion() + "<br />"); //v2
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   4. `this` keyword refers to the immediate parent object. If you 
       nest the object through function prototype, then `this` inside 
       object refers to the nested object not the function instance
*********************************************************************/
functionDef.prototype.nestedObj = { name: 'nestedObj', 
                                    getName1 : function(){
                                        return this+":"+this.name;
                                    }                            
                                  };

document.write(obj2.nestedObj.getName1() + "<br />"); //[object Object]:nestedObj

/********************************************************************* 
   5. If the method is on an object's prototype chain, `this` refers 
      to the object the method was called on, as if the method was on 
      the object.
*********************************************************************/
var ProtoObj = { fun: function () { return this.a } };
var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj
                                    //as its prototype
obj3.a = 999;                       //adding instance member to obj3
document.write(obj3.fun()+"<br />");//999
                                    //calling obj3.fun() makes 
                                    //ProtoObj.fun() to access obj3.a as 
                                    //if fun() is defined on obj3

4. W przypadku użycia wewnątrz funkcji konstruktora .

Gdy funkcja jest używana jako konstruktor (to znaczy, gdy jest wywoływana ze newsłowem kluczowym), thiswnętrze funkcji wskazuje na budowany nowy obiekt.

var myname = "global context";
function SimpleFun()
{
    this.myname = "simple function";
}

var obj1 = new SimpleFun(); //adds myname to obj1
//1. `new` causes `this` inside the SimpleFun() to point to the
//   object being constructed thus adding any member
//   created inside SimipleFun() using this.membername to the
//   object being constructed
//2. And by default `new` makes function to return newly 
//   constructed object if no explicit return value is specified

document.write(obj1.myname); //simple function

5. Przy zastosowaniu funkcji wewnętrznej zdefiniowanej w łańcuchu prototypów

Jeśli metoda znajduje się w łańcuchu prototypowym obiektu, thiswewnątrz tej metody odnosi się do obiektu, do którego została wywołana metoda, tak jakby metoda została zdefiniowana na obiekcie.

var ProtoObj = {
    fun: function () {
        return this.a;
    }
};
//Object.create() creates object with ProtoObj as its
//prototype and assigns it to obj3, thus making fun() 
//to be the method on its prototype chain

var obj3 = Object.create(ProtoObj);
obj3.a = 999;
document.write(obj3.fun()); //999

//Notice that fun() is defined on obj3's prototype but 
//`this.a` inside fun() retrieves obj3.a   

6. Wewnątrz funkcji call (), apply () i bind ()

  • Wszystkie te metody są zdefiniowane na Function.prototype.
  • Te metody pozwalają raz napisać funkcję i wywołać ją w innym kontekście. Innymi słowy, pozwalają określić wartość, thisktóra będzie używana podczas wykonywania funkcji. Przyjmują również wszelkie parametry, które mają zostać przekazane do oryginalnej funkcji, gdy jest ona wywoływana.
  • fun.apply(obj1 [, argsArray])Ustawia obj1jako wartość thisinside fun()i wywołuje fun()przekazywanie elementów argsArrayjako argumentów.
  • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])- Ustawia obj1jako wartość thiswewnątrz fun()i wywołuje fun()przekazywanie arg1, arg2, arg3, ...jako argumenty.
  • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])- Zwraca odniesienie do funkcji funz thiswewnętrzną zabawą powiązaną z obj1parametrami funzwiązanymi z określonymi parametrami arg1, arg2, arg3,....
  • Do tej pory różnica między apply, calli bindmusiało stać się widoczne. applypozwala określić argumenty, które mają funkcjonować jako obiekt tablicowy, tj. obiekt z lengthwłaściwością liczbową i odpowiadającymi mu nieujemnymi liczbami całkowitymi. callPozwala natomiast na bezpośrednie podanie argumentów funkcji. Zarówno applyi callnatychmiast wywołuje funkcję w określonym kontekście i przy użyciu określonych argumentów. Z drugiej strony bindpo prostu zwraca funkcję powiązaną z określoną thiswartością i argumentami. Możemy przechwycić odwołanie do tej zwróconej funkcji, przypisując ją do zmiennej, a później możemy wywołać ją w dowolnym momencie.
function add(inc1, inc2)
{
    return this.a + inc1 + inc2;
}

var o = { a : 4 };
document.write(add.call(o, 5, 6)+"<br />"); //15
      //above add.call(o,5,6) sets `this` inside
      //add() to `o` and calls add() resulting:
      // this.a + inc1 + inc2 = 
      // `o.a` i.e. 4 + 5 + 6 = 15
document.write(add.apply(o, [5, 6]) + "<br />"); //15
      // `o.a` i.e. 4 + 5 + 6 = 15

var g = add.bind(o, 5, 6);       //g: `o.a` i.e. 4 + 5 + 6
document.write(g()+"<br />");    //15

var h = add.bind(o, 5);          //h: `o.a` i.e. 4 + 5 + ?
document.write(h(6) + "<br />"); //15
      // 4 + 5 + 6 = 15
document.write(h() + "<br />");  //NaN
      //no parameter is passed to h()
      //thus inc2 inside add() is `undefined`
      //4 + 5 + undefined = NaN</code>

7. thiswewnątrz procedur obsługi zdarzeń

  • Gdy przypisujesz funkcję bezpośrednio do procedur obsługi zdarzeń elementu, użycie funkcji thisbezpośrednio wewnątrz funkcji obsługi zdarzeń odnosi się do odpowiedniego elementu. Takie bezpośrednie przypisanie funkcji można wykonać przy użyciu addeventListenermetody lub tradycyjnych metod rejestracji zdarzeń, takich jak onclick.
  • Podobnie, gdy używasz thisbezpośrednio wewnątrz właściwości zdarzenia (jak <button onclick="...this..." >) elementu, odnosi się on do elementu.
  • Jednak użycie thispośrednio przez inną funkcję wywoływaną wewnątrz funkcji obsługi zdarzenia lub właściwości zdarzenia odnosi się do obiektu globalnego window.
  • To samo powyższe zachowanie osiąga się, gdy dołączymy funkcję do procedury obsługi zdarzeń za pomocą metody modelu Microsoft Event Registration attachEvent. Zamiast przypisywać funkcję do procedury obsługi zdarzeń (a tym samym tworząc metodę funkcji elementu), wywołuje funkcję zdarzenia (skutecznie wywołując ją w kontekście globalnym).

Polecam lepiej wypróbować to w JSFiddle .

<script> 
    function clickedMe() {
       alert(this + " : " + this.tagName + " : " + this.id);
    } 
    document.getElementById("button1").addEventListener("click", clickedMe, false);
    document.getElementById("button2").onclick = clickedMe;
    document.getElementById("button5").attachEvent('onclick', clickedMe);   
</script>

<h3>Using `this` "directly" inside event handler or event property</h3>
<button id="button1">click() "assigned" using addEventListner() </button><br />
<button id="button2">click() "assigned" using click() </button><br />
<button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used `this` directly in click event property</button>

<h3>Using `this` "indirectly" inside event handler or event property</h3>
<button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">`this` used indirectly, inside function <br /> defined & called inside event property</button><br />

<button id="button4" onclick="clickedMe()">`this` used indirectly, inside function <br /> called inside event property</button> <br />

IE only: <button id="button5">click() "attached" using attachEvent() </button>

8. thisw funkcji strzałki ES6

W funkcji strzałki thiszachowuje się jak zmienne wspólne: zostanie odziedziczony z zakresu leksykalnego. Funkcja this, w której zdefiniowano funkcję strzałki, będzie funkcją strzałki this.

To jest to samo zachowanie, co:

(function(){}).bind(this)

Zobacz następujący kod:

const globalArrowFunction = () => {
  return this;
};

console.log(globalArrowFunction()); //window

const contextObject = {
  method1: () => {return this},
  method2: function(){
    return () => {return this};
  }
};

console.log(contextObject.method1()); //window

const contextLessFunction = contextObject.method1;

console.log(contextLessFunction()); //window

console.log(contextObject.method2()()) //contextObject

const innerArrowFunction = contextObject.method2();

console.log(innerArrowFunction()); //contextObject 

„Gdy używasz tego w funkcji zdefiniowanej w kontekście globalnym, nadal jest to powiązane z obiektem globalnym, ponieważ funkcja ta jest faktycznie metodą globalnego kontekstu”. jest nieprawidłowe. ten jest ustalany przez jaki wywoływana jest funkcja lub wiążą , a nie tam gdzie jest ona zdefiniowana. Wywołanie funkcji bez żadnego odniesienia bazowej ( „kontekst”) będzie domyślnie to do globalnego obiektu lub pozostają niezdefiniowane w trybie ścisłym.
RobG

@RobG hmm może być, ale znalazłem to w MDN : W tym przypadku wartość thisnie jest ustawiana przez wywołanie. Ponieważ kod nie jest w trybie ścisłym, wartość thismusi zawsze być obiektem, więc domyślnie jest to obiekt globalny. I właśnie dlatego myślałem, że możemy bezpośrednio zadzwonić window.f1(), więc oznacza f1()to , że środki są już dołączone do windowobiektu, to znaczy przed wywołaniem. Czy mylę się?
Mahesha999,

Byłem komentowania (być może nie jasno) na swoje powiązanie ustawienie to z „funkcja jest faktycznie metodę kontekście globalnym”, jakby to rodzaj nazywa window.fn, co nie jest. to domyślnie globalnego obiektu, ponieważ nie ma odniesienia zasadę stosowane w zaproszeniu, nie z powodu których funkcja jest określona (tak to jest nadal ustawiony przez jaki funkcja została wywołana). Jeśli jawnie wywołujesz go za pomocą window.fn, ustawiasz to na okno . Ten sam wynik, inny sposób postępowania. :-)
RobG

„powyżej umieściłem słowo natychmiast…” nie, nie zrobiłeś tego. Czy możesz to poprawić, aby naprawić błąd? Odpowiedź wydaje się semantyczna, dlatego nie mogę kontynuować czytania, dopóki błąd nie zostanie naprawiony z obawy przed nauczeniem się czegoś niepoprawnego.
TylerH

@TylerH do Ctrl + F na tej stronie w przeglądarce, aby znaleźć ciąg „natychmiastowy” (w tym podwójne cudzysłowy) Myślę, że jest tam, jeśli rozumiem, że źle
Mahesha999

64

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.

  1. Wywołanie funkcji ze newsłowem kluczowym natychmiast zainicjuje Objecttyp Person.
  2. Konstruktor tego Objectma ustawiony konstruktor Person. Pamiętaj też, że typeof awalwróci Objecttylko.
  3. 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.
  4. 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:

  1. Co jest thisdomyślnie przechowywane ?
  2. Co jeśli wywołamy funkcję jako metodę z notacją Object-dot?
  3. Co jeśli użyjemy new słowa kluczowego?
  4. Jak manipulujemy za thispomocą calli apply?
  5. Korzystanie bind.
  6. Kopiowanie w thiscelu rozwiązania problemów związanych z zagnieżdżeniem.

47

„to” dotyczy zakresu. Każda funkcja ma swój zakres, a ponieważ wszystko w JS jest obiektem, nawet funkcja może przechowywać w sobie pewne wartości za pomocą „this”. OOP 101 uczy, że „to” dotyczy tylko instancji obiektu. Dlatego za każdym razem, gdy funkcja jest wykonywana, nowa „instancja” tej funkcji ma nowe znaczenie „to”.

Większość ludzi jest zdezorientowana, gdy próbują użyć „tego” w funkcjach anonimowego zamknięcia, takich jak:

(funkcja (wartość) {
    this.value = wartość;
    $ ('. some-elements'). each (function (elt) {
        elt.innerHTML = this.value; // O o!! być może nieokreślony
    });
}) (2);

Więc tutaj, w każdym (), „to” nie zawiera „wartości”, której się spodziewasz (od

this.value = wartość;
ponad tym). Aby rozwiązać ten problem (bez zamierzonej gry słów), programista może:

(funkcja (wartość) {
    var self = this; // mała zmiana
    wartość własna = wartość;
    $ ('. some-elements'). each (function (elt) {
        elt.innerHTML = self.value; // uff !! == 2
    });
}) (2);

Wypróbuj to; spodoba ci się ten wzór programowania


6
„wszystko w JS jest obiektem” nie jest prawdą, JavaScript ma również prymitywne wartości, patrz bclary.com/2004/11/07/#a-4.3.2
Marcel Korpel

6
Prymitywne wartości wydają się mieć na sobie pewne metody, takie jak String # substring (), Number # toString () itp. Więc może nie mają takiej samej nomenklatury jak ten artykuł, naprawdę zachowują się tak, jakby były obiektami (są wszystkie prototypowane, tzn. String # substring () jest naprawdę: String.prototype.substring = function () {...}). Proszę popraw mnie jeżeli się mylę.
arunjitsingh

12
Słowo thiskluczowe nie ma nic wspólnego z zakresem. Ma to również znaczenie w funkcjach, które nie są właściwościami obiektów.
Bergi,

1
@ arunjitsingh - są na to dwie szkoły myślenia. Podoba mi się ten, który mówi: „ wszystko jest przedmiotem, ale niektóre mogą być reprezentowane przez prymitywy dla wygody ”. ;-)
RobG

9
thisnie chodzi wyłącznie o zakres. Chodzi o kontekst wykonania, który nie jest tym samym co zakres. JavaScript ma zasięg leksykalny (co oznacza, że ​​zasięg jest określony przez lokalizację kodu), ale thiszależy od tego, w jaki sposób wywoływana jest funkcja, która go zawiera - a nie, gdzie jest ta funkcja.
Scott Marcus

16

Odkąd ten wątek się podniósł, opracowałem kilka punktów dla czytelników, którzy nie znają thistematu.

Jak określa się wartość this?

Używamy tego w podobny sposób używamy zaimków w naturalnych języków takich jak angielski: „John pracuje szybko, ponieważ on próbuje złapać pociąg.” Zamiast tego moglibyśmy napisać „… John próbuje złapać pociąg”.

var person = {    
    firstName: "Penelope",
    lastName: "Barrymore",
    fullName: function () {

    // We use "this" just as in the sentence above:
       console.log(this.firstName + " " + this.lastName);

    // We could have also written:
       console.log(person.firstName + " " + person.lastName);
    }
}

this nie jest przypisywana wartość, dopóki obiekt nie wywoła funkcji, w której została zdefiniowana. W zakresie globalnym wszystkie zmienne globalne i funkcje są zdefiniowane na windowobiekcie. Dlatego thisw funkcji globalnej odnosi się do (i ma wartość) windowobiektu globalnego .

Kiedy use strict, thisw globalnym iw anonimowych funkcji, które nie są zobowiązane do dowolnego obiektu posiada wartość undefined.

Słowo thiskluczowe jest najbardziej niezrozumiane, gdy: 1) pożyczamy metodę, która używa this, 2) przypisujemy metodę, która używa thiszmiennej, 3) funkcja, która używa thisjest przekazywana jako funkcja zwrotna, i 4) thisjest używana wewnątrz zamknięcia - funkcja wewnętrzna. (2)

stół

Co trzyma przyszłość

Funkcje strzałki zdefiniowane w skrypcie ECMA 6 przejmują thispowiązanie z zakresu obejmującego (funkcja lub globalny).

function foo() {
     // return an arrow function
     return (a) => {
     // `this` here is lexically inherited from `foo()`
     console.log(this.a);
  };
}
var obj1 = { a: 2 };
var obj2 = { a: 3 };

var bar = foo.call(obj1);
bar.call( obj2 ); // 2, not 3!

Chociaż funkcje strzałek stanowią alternatywę dla używania bind(), ważne jest, aby pamiętać, że zasadniczo wyłączają tradycyjny thismechanizm na rzecz szerzej rozumianego zakresu leksykalnego. (1)


Bibliografia:

  1. this & Object Prototypes , autor: Kyle Simpson. © 2014 Getify Solutions.
  2. javascriptissexy.com - http://goo.gl/pvl0GX
  3. Angus Croll - http://goo.gl/Z2RacU

16

thisw JavaScript zawsze odnosi się do „właściciela” funkcji, który jest aktualnie wykonywany .

Jeśli nie zdefiniowano żadnego jawnego właściciela, wówczas odwołuje się do najwyższego właściciela, obiektu okna.

Więc gdybym to zrobił

function someKindOfFunction() {
   this.style = 'foo';
}

element.onclick = someKindOfFunction;

thisodnosi się do obiektu elementu. Ale bądź ostrożny, wiele osób popełnia ten błąd.

<element onclick="someKindOfFunction()">

W tym drugim przypadku odwołujesz się do funkcji, a nie do elementu. Dlatego thisbędzie odnosić się do obiektu okna.


15

Każdy kontekst wykonania w javascript ma ten parametr ustawiany przez:

  1. Jak wywoływana jest funkcja (w tym jako metoda obiektowa, użycie wywołania i zastosowania , użycie nowego )
  2. Zastosowanie wiązania
  3. Leksykalnie dla funkcji strzałek (przyjmują to z zewnętrznego kontekstu wykonania)
  4. Czy kod jest w trybie ścisłym, czy nie ścisłym
  5. Czy kod został wywołany przy użyciu eval

Możesz ustawić wartość tego za pomocą func.call, func.applylub func.bind.

Domyślnie, co wprawia w zakłopotanie większość początkujących, gdy detektor jest wywoływany po wywołaniu zdarzenia na elemencie DOM, wartością funkcji jest element DOM.

jQuery sprawia, że ​​zmiana tego trywialnego za pomocą jQuery.proxy jest łatwa.


9
Nieco bardziej poprawne jest stwierdzenie, że każde wywołanie funkcji ma zakres. Innymi słowy, thisw JavaScripcie mylące jest to, że nie jest to wewnętrzna właściwość samej funkcji, ale raczej artefakt sposobu jej wywoływania.
Pointy

@ pointy dzięki. tym, co powoduje największe zamieszanie w tym przypadku w js jest fakt, że we wszystkich używanych wcześniej językach (c #, c ++), - nie można nim manipulować n zawsze wskazuje na instancję obiektu, podczas gdy w js zależy i można go zmienić podczas wywoływania Używanie funkcji func.call, func.binditd. - Sushil
Sushil

2
thisczy nie odwołać zakresu danej funkcji. thisbędzie odnosić się do określonego obiektu (lub ewentualnie undefined), który, jak powiedziałeś, można zmienić za pomocą .call()lub .apply(). Funkcją jest zakres jest (zasadniczo, gdy uproszczone), które zmienne ma dostęp, a to zależy wyłącznie od tego, gdzie funkcja jest zadeklarowana i nie mogą być zmieniane.
nnnnnn

@Pointy: „Bardziej poprawne jest stwierdzenie, że każde wywołanie funkcji ma zakres.” Jeszcze bardziej poprawne jest stwierdzenie, że funkcje (a teraz bloki) mają zasięg , a wywołania funkcji mają kontekst . Zakres określa, jakie identyfikatory mogą być używane przez kod w tym zakresie. Kontekst określa, z czym powiązane są te identyfikatory.
TJ Crowder

1
„Cokolwiek to jest, jest określane przez„ to ”.” Nie, thisa zakres nie ma nic wspólnego ze sobą w ES5 i wcześniej (np. Kiedy napisano tę odpowiedź). W ES2015 (aka ES6) thisi zakresu są związane jeden dość minimalny sposób WRT strzałka funkcje (The thisw funkcji strzałki jest dziedziczona z jej zakresu okalającego), ale thisnie odnosi się do zakresu.
TJ Crowder

10

Tutaj jest dobrym źródłem thisw JavaScript.

Oto podsumowanie:

  • globalnie to

    W przeglądarce w zasięgu globalnym thisjest windowobiekt

    <script type="text/javascript">
      console.log(this === window); // true
      var foo = "bar";
      console.log(this.foo); // "bar"
      console.log(window.foo); // "bar"

    W nodeużyciu repl, thisjest top nazw. Możesz to nazwać global.

    >this
      { ArrayBuffer: [Function: ArrayBuffer],
        Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
        Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },
        ...
    >global === this
     true

    Podczas nodewykonywania ze skryptu thisw zakresie globalnym zaczyna się jako pusty obiekt. To nie to samo coglobal

    \\test.js
    console.log(this);  \\ {}
    console.log(this === global); \\ fasle
  • funkcja to

Z wyjątkiem przypadków obsługi zdarzeń DOM lub gdy thisArgpodano (patrz niżej), zarówno w węźle, jak iw przeglądarce używającej thisfunkcji, która nie jest wywoływana z newodniesieniami, zakres globalny…

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();
    console.log(this.foo); //logs "foo"
</script>

Jeśli skorzystasz use strict;, w takim przypadku thisbędzieundefined

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      "use strict";
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();  //Uncaught TypeError: Cannot set property 'foo' of undefined 
</script>

Jeśli wywołania funkcji z newtym thisbędzie nowym kontekście, nie będzie odwoływać się globalny this.

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    new testThis();
    console.log(this.foo); //logs "bar"

    console.log(new testThis().foo); //logs "foo"
</script>
  • prototypuj to

Tworzone funkcje stają się obiektami funkcji. Automatycznie otrzymują specjalną prototypewłaściwość, do której można przypisać wartości. Po utworzeniu instancji przez wywołanie funkcji newmasz dostęp do wartości przypisanych do prototypewłaściwości. Dostęp do tych wartości uzyskuje się za pomocą this.

function Thing() {
  console.log(this.foo);
}

Thing.prototype.foo = "bar";

var thing = new Thing(); //logs "bar"
console.log(thing.foo);  //logs "bar"

Zazwyczaj błędem jest przypisywanie tablic lub obiektów do prototype. Jeśli chcesz, aby instancje miały własne tablice, utwórz je w funkcji, a nie w prototypie.

function Thing() {
    this.things = [];
}

var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing1.things); //logs ["foo"]
console.log(thing2.things); //logs []
  • sprzeciwić się temu

Możesz użyć thisw dowolnej funkcji obiektu, aby odnieść się do innych właściwości tego obiektu. To nie to samo, co instancja utworzona za pomocą new.

var obj = {
    foo: "bar",
    logFoo: function () {
        console.log(this.foo);
    }
};

obj.logFoo(); //logs "bar"
  • Wydarzenie DOM to

W module obsługi zdarzeń DOM HTML thiszawsze jest odwołanie do elementu DOM, do którego zostało dołączone zdarzenie

function Listener() {
    document.getElementById("foo").addEventListener("click",
       this.handleClick);
}
Listener.prototype.handleClick = function (event) {
    console.log(this); //logs "<div id="foo"></div>"
}

var listener = new Listener();
document.getElementById("foo").click();

Chyba że bindkontekst

function Listener() {
    document.getElementById("foo").addEventListener("click", 
        this.handleClick.bind(this));
}
Listener.prototype.handleClick = function (event) {
    console.log(this); //logs Listener {handleClick: function}
}

var listener = new Listener();
document.getElementById("foo").click();
  • HTML to

Wewnątrz atrybutów HTML, w których można umieścić JavaScript, thisznajduje się odniesienie do elementu.

<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>
  • ewaluuj to

Możesz użyć, evalaby uzyskać dostęp this.

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    eval("console.log(this.foo)"); //logs "bar"
}

var thing = new Thing();
thing.logFoo();
  • z tym

Możesz użyć, withaby dodać thisdo bieżącego zakresu, aby odczytywać i zapisywać wartości thisbez odwoływania się thiswprost.

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    with (this) {
        console.log(foo);
        foo = "foo";
    }
}

var thing = new Thing();
thing.logFoo(); // logs "bar"
console.log(thing.foo); // logs "foo"
  • j Zapytaj o to

jQuery w wielu miejscach będzie thisodnosić się do elementu DOM.

<div class="foo bar1"></div>
<div class="foo bar2"></div>
<script type="text/javascript">
$(".foo").each(function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").on("click", function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").each(function () {
    this.click();
});
</script>

9

Daniel, niesamowite wyjaśnienie! Kilka słów na tej i dobrej liściethis wskaźnika kontekstu wykonania w przypadku procedur obsługi zdarzeń.

W dwóch słowach this w JavaScript wskazuje obiekt, od którego (lub z którego kontekstu wykonania) uruchomiono bieżącą funkcję i zawsze jest ona tylko do odczytu, i tak nie można jej ustawić (taka próba zakończy się błędem „Nieprawidłowa lewa ręka” komunikat „w zadaniu”.

W przypadku procedur obsługi zdarzeń: wbudowane procedury obsługi zdarzeń, takie jak <element onclick="foo">, zastępują wszelkie inne procedury obsługi dołączone wcześniej i wcześniej, więc należy zachować ostrożność i lepiej w ogóle unikać delegowania funkcji inline. I dzięki Zara Alaverdyan, która zainspirowała mnie do tej listy przykładów poprzez debatę odrębną :)

  • el.onclick = foo; // in the foo - obj
  • el.onclick = function () {this.style.color = '#fff';} // obj
  • el.onclick = function() {doSomething();} // In the doSomething - Window
  • el.addEventListener('click',foo,false) // in the foo - obj
  • el.attachEvent('onclick, function () { // this }') // window, all the compliance to IE :)
  • <button onclick="this.style.color = '#fff';"> // obj
  • <button onclick="foo"> // In the foo - window, but you can <button onclick="foo(this)">

9

Istnieje wiele nieporozumień dotyczących interpretacji „tego” słowa kluczowego w JavaScript. Mam nadzieję, że ten artykuł pozwoli wszystkim raz na zawsze odpocząć. I dużo więcej. Przeczytaj uważnie cały artykuł. Ostrzegam, że ten artykuł jest długi.

Niezależnie od kontekstu, w jakim jest używany, „to” zawsze odnosi się do „bieżącego obiektu” w JavaScript. Czym różni się „bieżący obiekt” w zależności od kontekstu . Kontekst może być dokładnie 1 z 6 poniższych sytuacji:

  1. Światowy (tj. Poza wszystkimi funkcjami)
  2. Wewnątrz bezpośredniego wywołania „funkcji niezwiązanej” (tj. Funkcji, która nie została powiązana przez wywołanie funkcji nazwa_pliku.bind )
  3. Wewnątrz Pośrednie „niezwiązane funkcje” Wywołanie poprzez functionName.call i functionName.apply
  4. Wewnątrz wywołania „Bound Function” (tj. Funkcja, która została powiązana przez wywołanie functionName.bind )
  5. Podczas tworzenia obiektu poprzez „nowy”
  6. Wewnątrz obsługi zdarzeń Inline DOM

Poniżej opisano każdy z tych kontekstów jeden po drugim:

  1. Kontekst globalny (tj. Poza wszystkimi funkcjami):

    Poza wszystkimi funkcjami (tj. W kontekście globalnym) „bieżący obiekt” (a zatem wartość „tego” ) jest zawsze obiektem „okna” dla przeglądarek.

  2. Wewnątrz bezpośredniego wywołania „funkcji niezwiązanej” :

    W bezpośrednim wywołaniu funkcji „niezwiązanej” obiekt, który wywołał wywołanie funkcji, staje się „bieżącym obiektem” (a zatem wartością „tego” ). Jeśli funkcja jest wywoływana bez jawnego bieżącego obiektu , bieżącym obiektem jest albo obiekt „okna” (w trybie ścisłym) lub niezdefiniowany (w trybie ścisłym). Każda funkcja (lub zmienna) zdefiniowana w Global Context automatycznie staje się własnością obiektu „window”. Na przykład Załóżmy, że funkcja jest zdefiniowana w Global Context jako

    function UserDefinedFunction(){
        alert(this)
        }

    staje się własnością obiektu okna, tak jakbyś go zdefiniował jako

    window.UserDefinedFunction=function(){
      alert(this)
    }  

    W „Trybie ścisłym” Wywołanie / wywołanie tej funkcji bezpośrednio przez „UserDefinedFunction ()” automatycznie wywołuje / wywołuje ją jako „window.UserDefinedFunction ()„ tworząc ”okno jako „ bieżący obiekt ” (i stąd wartość „ this ” ) w ramach „ UserDefinedFunction ”. Wywołanie tej funkcji w„ Non Strict Mode ”spowoduje:

    UserDefinedFunction() // displays [object Window]  as it automatically gets invoked as window.UserDefinedFunction()

    W „Strict Mode”, nazywając / Wywoływanie funkcji bezpośrednio poprzez „UserDefinedFunction ()” będzie „NIE” automatycznie zadzwoni / wywołać ją jako „window.UserDefinedFunction ()” .Hence „bieżący obiekt” (i wartość „to” ) w ramach „UserDefinedFunction” będzie niezdefiniowany . Wywołanie tej funkcji w „trybie ścisłym” spowoduje:

    UserDefinedFunction() // displays undefined

    Jednak jawne wywołanie go przy użyciu obiektu okna spowoduje:

    window.UserDefinedFunction() // "always displays [object Window]   irrespective of mode."

    Spójrzmy na inny przykład. Proszę spojrzeć na następujący kod

     function UserDefinedFunction()
        {
            alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)
        }
    
    var o1={
                a:1,
                b:2,
                f:UserDefinedFunction
          }
    var o2={
                c:3,
                d:4,
                f:UserDefinedFunction
           }
    
    o1.f() // Shall display 1,2,undefined,undefined
    o2.f() // Shall display undefined,undefined,3,4

    W powyższym przykładzie widzimy, że gdy „UserDefinedFunction” została wywołana przez o1 , „to” przyjmuje wartość o1 i wyświetla się wartość jej właściwości „a” i „b” . Wartości „c” i „d” zostały pokazane jako niezdefiniowane, ponieważ o1 nie definiuje tych właściwości

    Podobnie, gdy „UserDefinedFunction” została wywołana przez o2 , „to” przyjmuje wartość z O2 , a wartość jego właściwości „c” i „d” się displayed.The wartość „a” i „b” przedstawiono jako niezdefiniowane jako O2 czy nie definiuj tych właściwości.

  3. Wewnątrz Pośrednie „niezwiązane funkcje” Wywołanie poprzez functionName.call i functionName.apply :

    Kiedy „funkcja niezwiązana” jest wywoływana przez functionName.call lub functionName.apply , „bieżący obiekt” (i stąd wartość „this” ) jest ustawiany na wartość parametru „this” (pierwszy parametr) przekazywanego do wywołania / ubiegać się . Poniższy kod pokazuje to samo.

    function UserDefinedFunction()
    {
        alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)
    }
    var o1={
                a:1,
                b:2,
                f:UserDefinedFunction
           }
    var o2={
                c:3,
                d:4,
                f:UserDefinedFunction
           }
    
    UserDefinedFunction.call(o1) // Shall display 1,2,undefined,undefined
    UserDefinedFunction.apply(o1) // Shall display 1,2,undefined,undefined
    
    UserDefinedFunction.call(o2) // Shall display undefined,undefined,3,4
    UserDefinedFunction.apply(o2) // Shall display undefined,undefined,3,4
    
    o1.f.call(o2) // Shall display undefined,undefined,3,4
    o1.f.apply(o2) // Shall display undefined,undefined,3,4
    
    o2.f.call(o1) // Shall display 1,2,undefined,undefined
    o2.f.apply(o1) // Shall display 1,2,undefined,undefined

    Powyższy kod wyraźnie pokazuje, że wartość „ta” dla dowolnej „funkcji niezwiązanej” może zostać zmieniona poprzez wywołanie / zastosowanie . Ponadto, jeśli parametr „ten” nie zostanie jawnie przekazany do wywołania / zastosowania , „bieżący obiekt” (a zatem wartość „tego”) zostanie ustawiona na „okno” w trybie Nieokreślonym i „niezdefiniowany” w trybie ścisłym.

  4. Wewnątrz wywołania „Bound Function” (tj. Funkcja, która została powiązana przez wywołanie functionName.bind ):

    Funkcja powiązana to funkcja, której wartość „ta” została naprawiona. Poniższy kod pokazał, jak działa „to” w przypadku funkcji powiązanej

    function UserDefinedFunction()
    {
        alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)
    }
    var o1={
              a:1,
              b:2,
              f:UserDefinedFunction,
              bf:null
           }
    var o2={
               c:3,
               d:4,
               f:UserDefinedFunction,
               bf:null
            }
    
    var bound1=UserDefinedFunction.bind(o1); // permanantly fixes "this" value of function "bound1" to Object o1
    bound1() // Shall display 1,2,undefined,undefined
    
    var bound2=UserDefinedFunction.bind(o2); // permanantly fixes "this" value of function "bound2" to Object o2
    bound2() // Shall display undefined,undefined,3,4
    
    var bound3=o1.f.bind(o2); // permanantly fixes "this" value of function "bound3" to Object o2
    bound3() // Shall display undefined,undefined,3,4
    
    var bound4=o2.f.bind(o1); // permanantly fixes "this" value of function "bound4" to Object o1
    bound4() // Shall display 1,2,undefined,undefined
    
    o1.bf=UserDefinedFunction.bind(o2) // permanantly fixes "this" value of function "o1.bf" to Object o2
    o1.bf() // Shall display undefined,undefined,3,4
    
    o2.bf=UserDefinedFunction.bind(o1) // permanantly fixes "this" value of function "o2.bf" to Object o1
    o2.bf() // Shall display 1,2,undefined,undefined
    
    bound1.call(o2) // Shall still display 1,2,undefined,undefined. "call" cannot alter the value of "this" for bound function
    
    bound1.apply(o2) // Shall still display 1,2,undefined,undefined. "apply" cannot alter the value of "this" for bound function
    
    o2.bf.call(o2) // Shall still display 1,2,undefined,undefined. "call" cannot alter the value of "this" for bound function
    o2.bf.apply(o2) // Shall still display 1,2,undefined,undefined."apply" cannot alter the value of "this" for bound function

    Jak podano w powyższym kodzie, „tej” wartości dla dowolnej „funkcji powiązanej” NIE MOŻNA zmienić za pomocą wywołania / zastosowania . Ponadto, jeśli parametr „ten” nie zostanie jawnie przekazany do powiązania, „bieżący obiekt” (a zatem wartość „tego” ) zostanie ustawiona na „okno” w trybie Nieokreślone i „niezdefiniowany” w trybie ścisłym. Jeszcze jedna rzecz. Powiązanie już powiązanej funkcji nie zmienia wartości „this” . Pozostaje ustawiony jako wartość ustawiona przez pierwszą funkcję wiązania.

  5. Podczas tworzenia obiektu poprzez „nowy” :

    Wewnątrz funkcji konstruktora „bieżący obiekt” (i stąd wartość „tego” ) odnosi się do obiektu, który jest obecnie tworzony przez „nowy”, niezależnie od statusu powiązania funkcji. Jednak jeśli konstruktor jest funkcją powiązaną, zostanie wywołany z predefiniowanym zestawem argumentów ustawionym dla funkcji powiązanej.

  6. Wewnątrz modułu obsługi zdarzeń Inline DOM :

    Proszę spojrzeć na następujący fragment kodu HTML

    <button onclick='this.style.color=white'>Hello World</button>
    <div style='width:100px;height:100px;' onclick='OnDivClick(event,this)'>Hello World</div>

    „To” w Powyższe przykłady odnoszą się do „przycisku” elementu i „div” elementu odpowiednio.

    W pierwszym przykładzie kolor czcionki przycisku powinien być ustawiony na biały po kliknięciu.

    W drugim przykładzie, gdy kliknięty zostanie element „div” , powinien on wywołać funkcję OnDivClick z drugim parametrem odnoszącym się do klikniętego elementu div. Jednak wartość „this” w obrębie OnDivClick NIE MOŻE odnosić się do klikniętego elementu div . Powinien on być ustawiony jako „obiektu window” lub „nieokreślony” w Non ścisłych i Strict trybów odpowiednio (jeśli OnDivClick jest niezwiązany funkcja ) lub zestawu do predefiniowanej wartości stopniu (jeśli OnDivClick jest związany funkcja )

Poniżej podsumowano cały artykuł

  1. W kontekście globalnym „to” zawsze odnosi się do „okna” obiektu

  2. Za każdym razem, gdy wywoływana jest funkcja, wywoływana jest w kontekście obiektu ( „bieżący obiekt” ). Jeśli bieżący obiekt nie jest jawnie podany, bieżącym obiektem jest „obiekt okna” w trybie NIE STRIC i domyślnie „niezdefiniowany” w trybie Strict.

  3. Wartość „this” w funkcji Non Bound to odwołanie do obiektu, w kontekście którego funkcja jest wywoływana ( „bieżący obiekt” )

  4. Wartość „this” w funkcji Non Bound może zostać zastąpiona przez wywołanie i zastosowanie metod funkcji.

  5. Wartość „this” jest stała dla funkcji Bound i nie może być zastąpiona przez wywołanie i zastosowanie metod funkcji.

  6. Wiążąca i już powiązana funkcja nie zmienia wartości „this”. Pozostaje ustawiony jako wartość ustawiona przez pierwszą funkcję wiązania.

  7. Wartość „this” w konstruktorze to obiekt, który jest tworzony i inicjowany

  8. Wartość „this” w wbudowanym module obsługi zdarzeń DOM odnosi się do elementu, dla którego moduł obsługi zdarzeń jest podany.


9

Prawdopodobnie najbardziej szczegółowy i wyczerpujący artykuł thisjest następujący:

Delikatne objaśnienie „tego” słowa kluczowego w JavaScript

Chodzi o thisto, aby zrozumieć, że typy wywoływania funkcji mają istotne znaczenie dla ustawienia thiswartości.


Kiedy kłopoty identyfikacji this, nie należy zadać sobie pytanie:

Skąd thispochodzi ?

ale nie należy zadać sobie pytanie:

Jak wywoływana jest funkcja ?

W przypadku funkcji strzałki (szczególny przypadek przezroczystości kontekstu) zadaj sobie następujące pytanie:

Jaką wartość ma thistam, gdzie zdefiniowano funkcję strzałki ?

Ten sposób myślenia jest prawidłowy, gdy thismasz do czynienia z, i uratuje cię od bólu głowy.


Oprócz linków do swojego bloga, może zagłębisz się w to, w jaki sposób zadawanie tych pytań pomaga komuś zrozumieć thissłowo kluczowe?
Magnus Lind Oxlund,

7

Oto najlepsze wytłumaczenie, jakie widziałem: Zrozum to JavaScript z Clarity

To odniesienie zawsze odnosi się do (i posiada wartość) obiektowy osobliwy obiektowego i jest zwykle używany wewnątrz funkcji lub metody, ale można go stosować poza funkcją w zasięgu globalnym. Zauważ, że gdy korzystamy z trybu ścisłego, ma to wartość niezdefiniowaną w funkcjach globalnych i funkcjach anonimowych, które nie są powiązane z żadnym obiektem.

Istnieją cztery scenariusze, w których może to być mylące:

  1. Kiedy mijamy metodę (która korzysta z tego ) jako argument, który ma być użyty jako funkcja zwrotna.
  2. Kiedy używamy funkcji wewnętrznej (zamknięcia). Ważne jest, aby pamiętać, że zamknięcia nie mogą uzyskać dostępu do tej zmiennej funkcji zewnętrznej za pomocą tego słowa kluczowego, ponieważ ta zmienna jest dostępna tylko przez samą funkcję, a nie przez funkcje wewnętrzne.
  3. Gdy metoda, która polega na tym, jest przypisywana do zmiennej w różnych kontekstach, w którym to przypadku odnosi się do innego obiektu niż pierwotnie zamierzano.
  4. Podczas korzystania z tego wraz z metodami powiązania, zastosowania i wywołania.

Podaje przykłady kodu, wyjaśnienia i rozwiązania, które moim zdaniem były bardzo pomocne.


6

Mówiąc pseudoklasycznie, sposób, w jaki wiele wykładów uczy słowa kluczowego „this”, jest jak obiekt utworzony przez konstruktor klasy lub obiektu. Za każdym razem, gdy z klasy tworzony jest nowy obiekt, wyobraź sobie, że pod maską tworzona jest i zwracana lokalna instancja obiektu „tego”. Pamiętam, że uczyłem tak:

function Car(make, model, year) {
var this = {}; // under the hood, so to speak
this.make = make;
this.model = model;
this.year = year;
return this; // under the hood
}

var mycar = new Car('Eagle', 'Talon TSi', 1993);
// ========= under the hood
var this = {};
this.make = 'Eagle';
this.model = 'Talon TSi';
this.year = 1993;
return this;

5

thisjest jedną z źle zrozumianych koncepcji w JavaScript, ponieważ zachowuje się nieco inaczej w zależności od miejsca. Po prostu thisodnosi się do „właściciela” funkcji, którą obecnie wykonujemy .

thispomaga uzyskać bieżący obiekt (inaczej kontekst wykonania), z którym współpracujemy. Jeśli zrozumiesz, w którym obiekcie wykonywana jest bieżąca funkcja, możesz łatwo zrozumieć, co thisto jest prąd

var val = "window.val"

var obj = {
    val: "obj.val",
    innerMethod: function () {
        var val = "obj.val.inner",
            func = function () {
                var self = this;
                return self.val;
            };

        return func;
    },
    outerMethod: function(){
        return this.val;
    }
};

//This actually gets executed inside window object 
console.log(obj.innerMethod()()); //returns window.val

//Breakdown in to 2 lines explains this in detail
var _inn = obj.innerMethod();
console.log(_inn()); //returns window.val

console.log(obj.outerMethod()); //returns obj.val

Powyżej tworzymy 3 zmienne o tej samej nazwie „val”. Jeden w kontekście globalnym, jeden wewnątrz obj, a drugi wewnątrz innerMethod obj. JavaScript rozpoznaje identyfikatory w określonym kontekście, przechodząc w górę łańcucha zasięgu od lokalnego go global.


Niewiele miejsc, w których thismożna różnicować

Wywołanie metody obiektu

var status = 1;
var helper = {
    status : 2,
    getStatus: function () {
        return this.status;
    }
};

var theStatus1 = helper.getStatus(); //line1
console.log(theStatus1); //2

var theStatus2 = helper.getStatus;
console.log(theStatus2()); //1

Kiedy wiersz 1 jest wykonywany, JavaScript ustanawia kontekst wykonania (EC) dla wywołania funkcji, ustawiając thisna obiekt , do którego odwołuje się to, co pojawiło się przed ostatnim „.” . więc w ostatnim wierszu możesz zrozumieć, że a()został wykonany w kontekście globalnym, którym jest window.

Z konstruktorem

this może służyć do odniesienia do tworzonego obiektu

function Person(name){
    this.personName = name;
    this.sayHello = function(){
        return "Hello " + this.personName;
    }
}

var person1 = new Person('Scott');
console.log(person1.sayHello()); //Hello Scott

var person2 = new Person('Hugh');
var sayHelloP2 = person2.sayHello;
console.log(sayHelloP2()); //Hello undefined

Po Person()wykonaniu nowego tworzony jest całkowicie nowy obiekt. Personjest wywoływany, a jego thiszestaw jest ustawiony tak, aby odwoływał się do tego nowego obiektu.

Wywołanie funkcji

function testFunc() {
    this.name = "Name";
    this.myCustomAttribute = "Custom Attribute";
    return this;
}

var whatIsThis = testFunc();
console.log(whatIsThis); //window

var whatIsThis2 = new testFunc();
console.log(whatIsThis2);  //testFunc() / object

console.log(window.myCustomAttribute); //Custom Attribute 

Jeśli przegapimy newsłowo kluczowe, whatIsThisodnosi się do najbardziej globalnego kontekstu, jaki może znaleźć ( window)

Z modułami obsługi zdarzeń

Jeśli moduł obsługi zdarzeń jest wbudowany, thisoznacza obiekt globalny

<script type="application/javascript">
    function click_handler() {
        alert(this); // alerts the window object
    }
</script>

<button id='thebutton' onclick='click_handler()'>Click me!</button>

Podczas dodawania procedury obsługi zdarzeń za pomocą JavaScript thisodnosi się do elementu DOM, który wygenerował zdarzenie.



5

Wartość „this” zależy od „kontekstu”, w którym funkcja jest wykonywana. Kontekstem może być dowolny obiekt lub obiekt globalny, tj. Okno.

Semantyka „tego” różni się od tradycyjnych języków OOP. Powoduje to problemy: 1. gdy funkcja jest przekazywana do innej zmiennej (najprawdopodobniej wywołanie zwrotne); oraz 2. gdy zamknięcie jest wywoływane z metody członkowskiej klasy.

W obu przypadkach jest to ustawione na okno.


3

Czy to może pomóc? (Najbardziej nieporozumienie „tego” w javascript wynika z faktu, że zasadniczo nie jest ono powiązane z twoim obiektem, ale z bieżącym zakresem wykonywania - może nie tak dokładnie działa, ale zawsze wydaje mi się, że tak jest - zobacz artykuł, aby uzyskać pełne wyjaśnienie)


1
Lepiej byłoby powiedzieć, że jest „powiązany z bieżącym kontekstem wykonania ”. Z wyjątkiem zmian wprowadzonych w ES6 (wersja robocza), które zostały wprowadzone za pomocą funkcji strzałek, gdzie jest to rozwiązane w zewnętrznym kontekście wykonania.
RobG

3

Trochę informacji na ten temat słowie kluczowym

Zalogujmy thissłowo kluczowe do konsoli w zasięgu globalnym bez żadnego kodu, ale

console.log(this)

W kliencie / przeglądarce this słowo kluczowe jest globalnym obiektemwindow

console.log(this === window) // true

i

W środowisku wykonawczym Server / Node / JavaScript this słowo kluczowe jest również obiektem globalnymmodule.exports

console.log(this === module.exports) // true
console.log(this === exports) // true

Pamiętaj, exportsto tylko odniesienie domodule.exports


1

takie zastosowanie do Scope właśnie tak

  <script type="text/javascript" language="javascript">
$('#tbleName tbody tr').each(function{
var txt='';
txt += $(this).find("td").eq(0).text();
\\same as above but synatx different
var txt1='';
 txt1+=$('#tbleName tbody tr').eq(0).text();
alert(txt1)
});
</script>

wartość txt1 i txt jest taka sama w powyższym przykładzie $ (this) = $ ('# tbleName tbody tr') jest taki sam


1

Mam inne podejście this niż inne odpowiedzi, które, mam nadzieję, są pomocne.

Jednym ze sposobów patrzenia na JavaScript jest sprawdzenie, że istnieje tylko jeden sposób na wywołanie funkcji 1 . To jest

functionObject.call(objectForThis, arg0, arg1, arg2, ...);

Zawsze jest podana pewna wartość objectForThis.

Cała reszta to cukier składniowy functionObject.call

Tak więc wszystko inne można opisać przez to, jak się tłumaczy functionObject.call.

Jeśli wywołujesz funkcję, thisjest to „obiekt globalny”, który w przeglądarce jest oknem

function foo() {
  console.log(this);
}

foo();  // this is the window object

Innymi słowy,

foo();

został skutecznie przetłumaczony na

foo.call(window);

Pamiętaj, że jeśli użyjesz trybu ścisłego, thisbędzieundefined

'use strict';

function foo() {
  console.log(this);
}

foo();  // this is the window object

co znaczy

Innymi słowy,

foo();

został skutecznie przetłumaczony na

foo.call(undefined);

W JavaScript istnieją operatorzy, jak +i -i *. Istnieje również operator kropki.

.Operatora w przypadku korzystania z funkcji po prawej i po lewej obiektu skuteczne „oznacza obiekt jako przejściathis funkcjonować.

Przykład

const bar = {
  name: 'bar',
  foo() { 
    console.log(this); 
  },
};

bar.foo();  // this is bar

Innymi słowy bar.foo()przekłada się naconst temp = bar.foo; temp.call(bar);

Zauważ, że nie ma znaczenia, jak funkcja została utworzona (głównie ...). Wszystkie przyniosą takie same wyniki

const bar = {
  name: 'bar',
  fn1() { console.log(this); },
  fn2: function() { console.log(this); },
  fn3: otherFunction,
};

function otherFunction() { console.log(this) };

bar.fn1();  // this is bar
bar.fn2();  // this is bar
bar.fn3();  // this is bar

Ponownie wszystkie te są po prostu cukrem syntaktycznym

{ const temp = bar.fn1; temp.call(bar); }
{ const temp = bar.fn2; temp.call(bar); }
{ const temp = bar.fn3; temp.call(bar); }

Kolejnym zmarszczkiem jest łańcuch prototypowy. Kiedy używasz a.bJavaScript, najpierw patrzy na obiekt, do którego odnosi się bezpośrednio awłaściwość b. Jeśli obiekt bnie zostanie znaleziony, JavaScript przeszuka prototyp obiektu, aby go znaleźć b.

Istnieją różne sposoby definiowania prototypu obiektu, najczęstszym w 2019 roku jest classsłowo kluczowe. Dla celów thisnie ma to jednak znaczenia. Liczy się to, że gdy szuka w obiekcie awłaściwości, bjeśli znajdzie właściwość bna obiekcie lub w swoim łańcuchu prototypów, jeśli bostatecznie stanie się funkcją, wówczas obowiązują te same zasady, co powyżej. bOdwołania do funkcji będą wywoływane przy użyciu callmetody i przekazywaniaa jako objectForThis, jak pokazano na początku tej odpowiedzi.

Teraz. Wyobraźmy sobie, że tworzymy funkcję, która jawnie ustawia się thisprzed wywołaniem innej funkcji, a następnie wywołuje ją za pomocą .operatora (kropka)

function foo() {
  console.log(this);
}

function bar() {
  const objectForThis = {name: 'moo'}
  foo.call(objectForThis);  // explicitly passing objectForThis
}

const obj = {
  bar,
};

obj.bar();  

Po tłumaczenia do użycia call, obj.bar()staje się const temp = obj.bar; temp.call(obj);. Kiedy wchodzimy do barfunkcji, wywołujemy, fooale jawnie przekazujemy inny obiekt dla obiektu objectForThis, więc kiedy dochodzimy do foothis jest to wewnętrzny obiekt.

To jest to, co oboje bindi =>działa skutecznie zrobić. Są bardziej syntaktycznym cukrem. Skutecznie budują nową niewidzialną funkcję dokładnie tak, jak barpowyżej, która ustawia jawnie thisprzed wywołaniem dowolnej określonej funkcji. W przypadku powiązania thisjest ustawione na cokolwiek, co przekazujesz bind.

function foo() {
  console.log(this);
}

const bar = foo.bind({name: 'moo'});

// bind created a new invisible function that calls foo with the bound object.

bar();  

// the objectForThis we are passing to bar here is ignored because
// the invisible function that bind created will call foo with with
// the object we bound above

bar.call({name: 'other'});

Zauważ, że gdyby functionObject.bindnie istniały, moglibyśmy stworzyć własny w ten sposób

function bind(fn, objectForThis) {
  return function(...args) {
    return fn.call(objectForthis, ...args);
  };
}

i moglibyśmy to tak nazwać

function foo() {
  console.log(this);
}

const bar = bind(foo, {name:'abc'});

Funkcje strzałek, =>operator to cukier syntaktyczny do wiązania

const a = () => {console.log(this)};

jest taki sam jak

const tempFn = function() {console.log(this)}; 
const a = tempFn.bind(this);

Podobnie bind, tworzona jest nowa niewidzialna funkcja, która wywołuje daną funkcję z powiązaną wartością, objectForThisale w przeciwieństwie binddo obiektu, który ma być związany, jest niejawna. Tak thisdzieje się, gdy =>operator jest używany.

Podobnie jak powyższe zasady

const a = () => { console.log(this); }  // this is the global object
'use strict';
const a = () => { console.log(this); }  // this is undefined
function foo() {
  return () => { console.log(this); }
}

const obj = {
  foo,
};
const b = obj.foo();
b();

obj.foo()tłumaczy, na const temp = obj.foo; temp.call(obj);co oznacza, że ​​operator strzałki wewnątrz foowiąże objsię z nową niewidzialną funkcją i zwraca tę nową niewidzialną funkcję, do której jest przypisana b. b()będzie działać tak, jak zawsze ma, b.call(window)lub b.call(undefined)wywołuje nową, niewidoczną funkcję, która została fooutworzona. Ta niewidoczna funkcja ignoruje thisprzekazane do niej i przechodzi objjako objectForThis do funkcji strzałki.

Powyższy kod tłumaczy się na

function foo() {
  function tempFn() {
    console.log(this);
  }
  return tempFn.bind(this);
}

const obj = {
  foo,
};
const b = obj.foo();
b.call(window or undefined if strict mode);

1apply to kolejna funkcja podobna docall

functionName.apply(objectForThis, arrayOfArgs);

Ale od ES6 koncepcyjnie możesz to nawet przełożyć na

functionName.call(objectForThis, ...arrayOfArgs);

0

Podsumowanie thisJavascript:

  • Wartość thiszależy od sposobu, w jaki funkcja nie jest wywoływana, gdzie została utworzona!
  • Zwykle wartość thisjest określana przez obiekt, który znajduje się po kropce. ( windoww przestrzeni globalnej)
  • W detektorach zdarzeń wartość parametru this odnosi się do elementu DOM, na którym wywołano zdarzenie.
  • Gdy funkcja jest wywoływana ze newsłowem kluczowym, wartość thisodnosi się do nowo utworzonego obiektu
  • Można manipulować wartość thisz funkcjami: call, apply,bind

Przykład:

let object = {
  prop1: function () {console.log(this);}
}

object.prop1();   // object is left of the dot, thus this is object

const myFunction = object.prop1 // We store the function in the variable myFunction

myFunction(); // Here we are in the global space
              // myFunction is a property on the global object
              // Therefore it logs the window object
              
             

Przykładowe detektory zdarzeń:

document.querySelector('.foo').addEventListener('click', function () {
  console.log(this);   // This refers to the DOM element the eventListener was invoked from
})


document.querySelector('.foo').addEventListener('click', () => {
  console.log(this);  // Tip, es6 arrow function don't have their own binding to the this v
})                    // Therefore this will log the global object
.foo:hover {
  color: red;
  cursor: pointer;
}
<div class="foo">click me</div>

Przykład konstruktora:

function Person (name) {
  this.name = name;
}

const me = new Person('Willem');
// When using the new keyword the this in the constructor function will refer to the newly created object

console.log(me.name); 
// Therefore, the name property was placed on the object created with new keyword.


0

Aby właściwie to zrozumieć, trzeba zrozumieć kontekst, zakres i różnicę między nimi.

Zakres : w javascript zakres związany jest z widocznością zmiennych, zakres osiąga się poprzez użycie funkcji. (Przeczytaj więcej o zakresie)

Kontekst : Kontekst jest powiązany z obiektami. Odnosi się do obiektu, do którego należy funkcja. Kiedy używasz słowa kluczowego JavaScript „this”, odnosi się do obiektu, do którego należy funkcja. Na przykład wewnątrz funkcji, gdy mówisz: „this.accoutNumber”, masz na myśli właściwość „accoutNumber”, która należy do obiektu, do którego należy ta funkcja.

Jeśli obiekt „myObj” ma metodę o nazwie „getMyName”, to gdy słowo kluczowe „this” jest używane w „getMyName”, odnosi się do „myObj”. Jeśli funkcja „getMyName” została wykonana w zasięgu globalnym, wówczas „to” odnosi się do obiektu okna (z wyjątkiem trybu ścisłego).

Zobaczmy teraz przykład:

    <script>
        console.log('What is this: '+this);
        console.log(this);
    </script>

Uruchomienie powyższego kodu w wynikach przeglądarki: wprowadź opis zdjęcia tutaj

Zgodnie z danymi wyjściowymi zawartymi w kontekście obiektu okna widać również, że prototyp okna odnosi się do obiektu.

Teraz spróbujmy wewnątrz funkcji:

    <script>
        function myFunc(){
            console.log('What is this: '+this);
            console.log(this);
        }
        myFunc();
    </script>

Wynik:

wprowadź opis zdjęcia tutaj Dane wyjściowe są takie same, ponieważ zarejestrowaliśmy zmienną „this” w zakresie globalnym i zarejestrowaliśmy ją w zakresie funkcjonalnym, nie zmieniliśmy kontekstu. W obu przypadkach kontekst był taki sam, związany z obiektem wdowy .

Teraz stwórzmy własny obiekt. W javascript możesz utworzyć obiekt na wiele sposobów.

 <script>
        var firstName = "Nora";
        var lastName = "Zaman";
        var myObj = {
            firstName:"Lord",
            lastName:'Baron',
            printNameGetContext:function(){
                console.log(firstName + " "+lastName);
                console.log(this.firstName +" "+this.lastName);
                return this;
            }
        }

      var context = myObj.printNameGetContext();
      console.log(context);
    </script>

Wynik: wprowadź opis zdjęcia tutaj

Z powyższego przykładu wynika, że ​​słowo kluczowe „to” odnosi się do nowego kontekstu związanego z myObj, a myObject ma również łańcuch prototypów do Object.

Rzućmy inny przykład:

<body>
    <button class="btn">Click Me</button>
    <script>
        function printMe(){
            //Terminal2: this function declared inside window context so this function belongs to the window object.
            console.log(this);
        }
        document.querySelector('.btn').addEventListener('click', function(){
            //Terminal1: button context, this callback function belongs to DOM element 
            console.log(this);
            printMe();
        })
    </script>
</body>

wynik: czy ma sens, prawda? (czytaj komentarze) wprowadź opis zdjęcia tutaj

Jeśli masz problem ze zrozumieniem powyższego przykładu, spróbujmy z naszym oddzwonieniem;

<script>
        var myObj = {
            firstName:"Lord",
            lastName:'Baron',
            printName:function(callback1, callback2){
                //Attaching callback1 with this myObj context
                this.callback1 = callback1;
                this.callback1(this.firstName +" "+this.lastName)
                //We did not attached callback2 with myObj so, it's reamin with window context by default
                callback2();
                /*
                 //test bellow codes
                 this.callback2 = callback2;
                 this.callback2();
                */
            }
        }
        var callback2 = function (){
            console.log(this);
        }
        myObj.printName(function(data){
            console.log(data);
            console.log(this);
        }, callback2);
    </script>

wynik: wprowadź opis zdjęcia tutaj

Teraz zrozummy zakres, jaźń, IIFE i TO, jak się zachowuje

       var color = 'red'; // property of window
       var obj = {
           color:'blue', // property of window
           printColor: function(){ // property of obj, attached with obj
               var self = this;
               console.log('In printColor -- this.color: '+this.color);
               console.log('In printColor -- self.color: '+self.color);
               (function(){ // decleard inside of printColor but not property of object, it will executed on window context.
                    console.log(this)
                    console.log('In IIFE -- this.color: '+this.color);
                    console.log('In IIFE -- self.color: '+self.color); 
               })();

               function nestedFunc(){// decleard inside of printColor but not property of object, it will executed on window context.
                    console.log('nested fun -- this.color: '+this.color);
                    console.log('nested fun -- self.color: '+self.color);
               }

               nestedFunc(); // executed on window context
               return nestedFunc;
           }
       };

       obj.printColor()(); // returned function executed on window context
   </script> 

Wydajność jest niesamowita, prawda? wprowadź opis zdjęcia tutaj


-1

Prosta odpowiedź:

„to” słowo kluczowe zawsze zależy od kontekstu wywołania. Są one wymienione poniżej.

  1. FUNKCJA JEST WYWOŁANA NOWYM SŁOWEM KLUCZOWYM

    Jeśli funkcja zostanie wywołana ze słowem kluczowym NEW, TO zostanie przypisane do nowo utworzonego obiektu.

    function Car(){
      this.name="BMW";
    }
    const myCar=new Car();
    myCar.name; // output "BMW"

    W powyższym będzie to związane z obiektem „myCar”

  2. FUNKCJA JEST WYŁĄCZNIE WYKORZYSTANA Z WYKORZYSTANIEM POŁĄCZEŃ I STOSOWANIA.

    W takim przypadku TO zostanie powiązane z obiektem, który jest jawnie przekazany do funkcji.

    var obj1={"name":"bond"};
    function printMessage(msg){
        return msg+" "+this.name;
    }
    const message=printMessage.call(obj1,"my name is ");
    console.log(message); //HERE THIS WILL BE BOUND TO obj1 WHICH WE PASSED EXPLICITLY. SAME FOR APPLY METHOD ALSO.
  3. JEŻELI FUNKCJA JEST BEZWZGLĘDNIE OBOWIĄZKOWA, NINIEJSZYM BĘDZIE ZWIĄZANY Z TYM OBIEKTEM

    var obj1={
       "name":"bond",
        getName: function () {
                    return this.name;
                 }
    };
    const newname=obj1.getName();
    console.log(newname); //HERE THIS WILL BE BOUND TO obj1(WHITCHEVER OBJECT IS MENTIONED BEFORE THE DOT THIS WILL BE BOUND TO THAT)
  4. JEŻELI FUNKCJA JEST WYWOŁANA BEZ ŻADNEGO KONTEKSTU, NINIEJSZYM BĘDZIE ZWIĄZANA Z GLOBALNYM OBIEKTEM

    const util = {
       name: 'Utility',
       getName: function () {
                    return this.name;
    };
    
    const getName=util.getName;
    const newName=getName();
    console.log(newName); // IF THIS EXECUTED IN BROWSER THIS WILL BE  BOUND TO WINDOW OBJECT. IF THIS EXECUTED IN SERVER THIS WILL BE BOUND TO GLOBAL OBJECT
  5. W TRYBIE ŚCISŁOŚCI BĘDZIE NIE DANE

    function setName(name){
        "use strict"
        return this.name;
    }
    setName(); //WILL BE ERROR SAYING name IS UNDEFINED. 
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.