Właśnie spotkałem ciekawą sytuację w JavaScript. Mam klasę z metodą, która definiuje kilka obiektów za pomocą notacji literałowej. Wewnątrz tych obiektów this
używany jest wskaźnik. Na podstawie zachowania programu wydedukowałem, że this
wskaźnik odnosi się do klasy, na której wywołano metodę, a nie do obiektu tworzonego przez literał.
Wydaje się to arbitralne, ale tego właśnie oczekiwałbym. Czy to określone zachowanie? Czy to jest bezpieczne dla różnych przeglądarek? Czy jest jakieś uzasadnienie, dlaczego leży ono poza „specyfikacją tak mówi” (na przykład, czy jest to konsekwencja szerszej decyzji / filozofii projektu)? Przykład zredukowanego kodu:
// inside class definition, itself an object literal, we have this function:
onRender: function() {
this.menuItems = this.menuItems.concat([
{
text: 'Group by Module',
rptletdiv: this
},
{
text: 'Group by Status',
rptletdiv: this
}]);
// etc
}
var signup = { onLoadHandler:function(){ console.log(this); return Type.createDelegate(this,this._onLoad); }, _onLoad: function (s, a) { console.log("this",this); }};
Odpowiedzi:
Cannibalized z innego mojego postu, oto więcej niż kiedykolwiek chciałeś o tym wiedzieć .
Zanim zacznę, najważniejsze jest, aby pamiętać o JavaScript i powtarzać sobie, gdy nie ma to sensu. JavaScript nie ma klas (ES6
class
to cukier składniowy ). Jeśli coś wygląda jak klasa, to sprytna sztuczka. JavaScript ma obiekty i funkcje . (to nie jest w 100% dokładne, funkcje są tylko obiektami, ale czasem może być pomocne, aby traktować je jako osobne rzeczy)Ta zmienna jest przyłączona do funkcji. Kiedykolwiek wywołać funkcję, to otrzymuje pewną wartość, w zależności od tego, jak wywołać funkcji. Jest to często nazywane wzorem wywoływania.
Istnieją cztery sposoby wywoływania funkcji w javascript. Możesz wywoływać funkcję jako metodę , jako funkcję , jako konstruktor i z zastosowaniem .
Jako metoda
Metoda to funkcja dołączona do obiektu
Wywołany jako metody, to będzie zobowiązany do obiektu funkcja / metoda jest częścią. W tym przykładzie będzie to związane z foo.
Jako funkcja
Jeśli masz funkcję samodzielną, ta zmienna zostanie powiązana z obiektem „globalnym”, prawie zawsze obiektem okna w kontekście przeglądarki.
To może Cię potknąć , ale nie czuj się źle. Wiele osób uważa to za złą decyzję projektową. Ponieważ wywołanie zwrotne jest wywoływane jako funkcja, a nie metoda, dlatego widzisz to, co wydaje się być niespójnym zachowaniem.
Wiele osób rozwiązuje ten problem, robiąc coś takiego
Definiujesz zmienną , która wskazuje na to . Zamknięcie (temat sam w sobie) utrzymuje to w pobliżu, więc jeśli wywołasz bar jako oddzwanianie, nadal ma on odniesienie.
UWAGA: W
use strict
trybie, jeśli jest używany jako funkcja,this
nie jest powiązany z globalnym. (Tak jestundefined
).Jako konstruktor
Możesz także wywołać funkcję jako konstruktor. W oparciu o konwencję nazewnictwa, z której korzystasz (TestObject), może to być również to, co robisz i to Cię denerwuje .
Wywołujesz funkcję jako Konstruktor z nowym słowem kluczowym.
Wywołany jako konstruktor, nowy obiekt zostanie utworzony, a ten będzie związany z tym obiektem. Ponownie, jeśli masz funkcje wewnętrzne i są one używane jako wywołania zwrotne, będziesz wywoływać je jako funkcje, a to zostanie powiązane z obiektem globalnym. Użyj tego var that = ten trick / wzorzec.
Niektórzy uważają, że słowo kluczowe konstruktor / nowe było kością rzuconą na programistów Java / tradycyjnych programistów OOP jako sposób na stworzenie czegoś podobnego do klas.
Za pomocą metody Apply
Na koniec każda funkcja ma metodę (tak, funkcje są obiektami w JavaScript) o nazwie „zastosuj”. Zastosuj pozwala określić, jaka jest wartość tego będzie, a także pozwala przechodzić w tablicy argumentów. Oto bezużyteczny przykład.
źródło
this
będzieundefined
dla wywołań funkcji.this
nie ma nic wspólnego z zakresem. Masz na myśli obiekt globalny .this.confusing = 'hell yeah';
taki sam jakvar confusing = 'hell yeah';
? Więc oba pozwoląmyObject.confusing
? Byłoby miło, gdyby nie tylko po to, aby można było użyćthis
do utworzenia właściwości i innych zmiennych do pracy wewnętrznej.function Foo(thought){ this.confusing = thought; }
var myObject = new Foo("hell yeah");
Wywołania funkcji
Funkcje są tylko rodzajem obiektu.
Wszystkie obiekty Function mają metody wywołania i zastosowania , które wykonują wywołany obiekt Function.
Po wywołaniu pierwszy argument tych metod określa obiekt, do którego
this
słowo kluczowe będzie się odnosić podczas wykonywania funkcji - jeśli jest to obiekt globalnynull
lubundefined
, do któregowindow
jest on używanythis
.Wywołanie funkcji ...
... z nawiasami -
foo()
- jest równoważnefoo.call(undefined)
lubfoo.apply(undefined)
, co jest faktycznie takie samo jakfoo.call(window)
lubfoo.apply(window)
.Dodatkowe argumenty do
call
przekazania są przekazywane jako argumenty do wywołania funkcji, podczas gdy pojedynczy dodatkowy argument doapply
określenia argumentów dla wywołania funkcji jako obiekt podobny do tablicy.Zatem
foo(1, 2, 3)
jest równoważne zfoo.call(null, 1, 2, 3)
lubfoo.apply(null, [1, 2, 3])
.Jeśli funkcja jest własnością obiektu ...
... dostęp do odwołania do funkcji za pośrednictwem obiektu i wywołanie go w nawiasach -
obj.foo()
- jest równoważne zfoo.call(obj)
lubfoo.apply(obj)
.Jednak funkcje utrzymywane jako właściwości obiektów nie są „powiązane” z tymi obiektami. Jak widać w powyższej definicji
obj
, ponieważ Funkcje są tylko rodzajem Object, można się do nich odwoływać (a zatem mogą być przekazywane przez odwołanie do wywołania funkcji lub zwracane przez odwołanie z wywołania funkcji). Gdy odwołanie do funkcji jest przekazywana bez dodatkowych informacji o tym, gdzie został przekazany od przeprowadza z nim, dlatego dodaje się dzieje:Wywołanie naszego odwołania do funkcji
baz
nie zapewnia żadnego kontekstu dla wywołania, więc jest faktycznie takie samo jakbaz.call(undefined)
, więcthis
kończy się odwołaniemwindow
. Jeśli chcemybaz
wiedzieć, że należy do tej domenyobj
, musimy w jakiś sposób podać tę informację, kiedybaz
zostanie wywołana, czyli tam, gdzie wchodzi pierwszy argument docall
lubapply
zamknięcie.Łańcuchy zakresowe
Kiedy funkcja jest wykonywana, tworzy nowy zakres i ma odniesienie do dowolnego zakresu obejmującego. Gdy funkcja anonimowa jest tworzona w powyższym przykładzie, ma odniesienie do zakresu, w którym została utworzona, czyli
bind
zakresu. Jest to znane jako „zamknięcie”.Kiedy próbujesz uzyskać dostęp do zmiennej, ten „łańcuch zasięgu” idzie do znalezienia zmiennej o podanej nazwie - jeśli bieżący zakres nie zawiera zmiennej, patrzysz na następny zakres w łańcuchu i tak dalej, aż dojdziesz zasięg globalny. Kiedy funkcja anonimowa jest zwracana i
bind
kończy działanie, funkcja anonimowa nadal ma odniesienie dobind
zakresu, więcbind
zakres nie „znika”.Biorąc pod uwagę wszystkie powyższe, powinieneś być teraz w stanie zrozumieć, jak działa zakres w poniższym przykładzie i dlaczego technika przekazywania funkcji wokół „wstępnie powiązanej” z określoną wartością
this
będzie miała, gdy zostanie nazwana:źródło
Tak. I tak.
Znaczenie tego
this
jest dość proste, aby wydedukować:this
jest używany wewnątrz funkcji konstruktora, a funkcja została wywołana zenew
słowem kluczowym,this
oznacza obiekt, który zostanie utworzony.this
będzie nadal oznaczać obiekt nawet w metodach publicznych.this
jest używany gdziekolwiek indziej, w tym zagnieżdżone funkcje chronione , odnosi się do zasięgu globalnego (który w przypadku przeglądarki jest obiektem okna).Drugi przypadek jest oczywiście wadą projektową, ale dość łatwo można go obejść za pomocą zamknięć.
źródło
W tym przypadku wnętrze
this
jest powiązane z obiektem globalnym zamiast zethis
zmienną funkcji zewnętrznej. Jest to sposób projektowania języka.Dobre wyjaśnienie można znaleźć w „JavaScript: The Good Parts” Douglasa Crockforda.
źródło
Znalazłem fajny samouczek na temat ECMAScript
Jako tę wartość kontekstu można użyć dowolnego obiektu.
Ta funkcja jest bardzo ważna, ponieważ w przeciwieństwie do zmiennych, ta wartość nigdy nie bierze udziału w procesie rozpoznawania identyfikatora. Oznacza to, że podczas uzyskiwania dostępu do tego w kodzie jego wartość jest pobierana bezpośrednio z kontekstu wykonania i bez wyszukiwania łańcucha zasięgu. Wartość tego jest określana tylko raz podczas wchodzenia w kontekst.
W kontekście globalnym ta wartość jest samym obiektem globalnym (co oznacza, że ta wartość tutaj jest równa obiektowi zmiennemu)
W przypadku kontekstu funkcji ta wartość w każdym wywołaniu funkcji może być inna
Odwołaj się do Javascript-the-core i Rozdział-3-this
źródło