Czy możesz powiązać „to” w funkcji strzałki?

133

Od jakiegoś czasu eksperymentuję z ES6 i właśnie doszedłem do małego problemu.

Bardzo lubię używać funkcji strzałkowych i kiedy tylko mogę, używam ich.

Jednak wydawałoby się, że nie możesz ich związać!

Oto funkcja:

var f = () => console.log(this);

Oto obiekt, z którym chcę powiązać funkcję:

var o = {'a': 42};

A oto jak bym się związał fz o:

var fBound = f.bind(o);

A potem mogę po prostu zadzwonić fBound:

fBound();

Który wyświetli to ( oobiekt):

{'a': 42}

Chłodny! Śliczny! Tyle że to nie działa. Zamiast wyprowadzać oobiekt, wyprowadza windowobiekt.

Więc chciałbym wiedzieć: czy możesz powiązać funkcje strzałkowe? (A jeśli tak, to jak?)


Przetestowałem powyższy kod w Google Chrome 48 i Firefox 43 i wynik jest taki sam.

Florrie
źródło
29
Celem funkcji strzałkowych jest to, że używają thiszakresu nadrzędnego.
loganfsmyth

Odpowiedzi:

158

Nie można ponownie powiązać this funkcji strzałkowej. Będzie zawsze definiowany jako kontekst, w którym został zdefiniowany. Jeśli chcesz thisbyć sensowny, powinieneś użyć normalnej funkcji.

Ze specyfikacji ECMAScript 2015 :

Każde odwołanie do arguments, super, this lub new.target w ArrowFunction musi być rozstrzygnięte na powiązanie w środowisku obejmującym leksykalnie. Zwykle będzie to środowisko funkcyjne natychmiast obejmującej funkcję.

Nick Tomlin
źródło
7
Pierwsze zdanie tej odpowiedzi jest błędne. Prawdopodobnie dlatego, że pytanie jest również mylące, wiązanie i this. Te dwa są powiązane, ale nie to samo. thiswewnątrz funkcji strzałkowej zawsze „dziedziczy” thisz otaczającego zakresu. To jest cecha funkcji strzałkowych. Ale nadal możesz bindwszystkie inne parametry funkcji strzałkowej. Po prostu nie this.
Stijn de Witt
54

Aby być kompletny, to może ponownie wiążą strzałka funkcji, po prostu nie można zmienić znaczenie this.

bind nadal ma wartość dla argumentów funkcji:

((a, b, c) => {
  console.info(a, b, c) // 1, 2, 3
}).bind(undefined, 1, 2, 3)()

Wypróbuj tutaj: http://jsbin.com/motihanopi/edit?js,console

cvazac
źródło
1
Czy istnieje sposób na wykrycie lub użycie dowolnego obiektu, do thisktórego jest przypisana funkcja (strzałka), bez odwoływania się (co jest oczywiście zdefiniowane leksykalnie)?
Florrie
3
Użyj argumentu dla kontekstu: ((context) => {someOtherFunction.apply (context)}). Bind (willBeIgnored, context) ()
cvazac,
13

Z MDN :

Wyrażenie funkcji strzałkowej ma krótszą składnię w porównaniu z wyrażeniami funkcyjnymi i leksykalnie wiąże wartość this (nie wiąże własnej wartości this, arguments, super lub new.target). Funkcje strzałkowe są zawsze anonimowe.

Oznacza to, że nie możesz przypisać wartości thistak, jak chcesz.

Sterling Archer
źródło
6

Przez lata programiści js zmagali się z wiązaniem kontekstu, pytali, dlaczego thiszmienił się w javascript, tyle zamieszania przez lata z powodu wiązania kontekstu i różnicy między znaczeniem thisw javascript i thisw większości innych języków OOP.

Wszystko to prowadzi mnie do pytania, dlaczego, dlaczego! dlaczego miałbyś chcieć ponownie powiązać funkcję strzały! Te, w których tworzone specjalnie, aby rozwiązać wszystkie te problemy i niejasności i uniknąć konieczności użycia bindlub callczy cokolwiek innego sposobu, aby zachować zakres funkcji.

TL; DR

Nie, nie możesz ponownie powiązać funkcji strzałkowych.

taxicala
źródło
7
Funkcje strzałkowe zostały stworzone przede wszystkim w celu zapewnienia bardziej przejrzystej i szybszej składni. Pomyśl o rachunku lambda. Szkoda, że ​​zwolennicy OO, którzy dręczą życie programistów funkcjonalnych JS, skorzystali z okazji, aby odebrać im swobodę określania kontekstu wywołania.
Marco Faustinelli
5
Używanie thisnie jest funkcjonalne. lambdy powinny być arguments => output. Jeśli potrzebujesz jakiegoś zewnętrznego kontekstu, podaj go. Samo istnienie thisjest tym, co ułatwiło wprowadzenie do języka wzorców obiektowych. Bez niego nigdy nie słyszałbyś terminu „klasa javascript”.
erich2k8
3
Państwo może ponownie powiązać funkcji strzałek. Po prostu nie this.
Stijn de Witt
6

Nie można użyć binddo zmiany wartości thiswewnątrz funkcji strzałkowej. Możesz jednak utworzyć nową zwykłą funkcję, która robi to samo, co stara funkcja strzałkowa, a następnie użyć calllub w bindcelu ponownego powiązania thisjak zwykle.

Używamy evaltutaj wywołania, aby odtworzyć funkcję strzałki, którą przekazujesz jako normalną funkcję, a następnie używamy calldo wywołania jej z inną this:

var obj = {value: 10};
function arrowBind(context, fn) {
  let arrowFn;
  (function() {
    arrowFn = eval(fn.toString());
    arrowFn();
  }).call(context);
}
arrowBind(obj, () => {console.log(this)});

imię
źródło
3
Dziękujemy za udostępnienie tego przykładu sposobu wiązania funkcji strzałki w innym kontekście! Aby odpowiedź była łatwiejsza do zrozumienia dla przyszłych czytelników, być może mógłbyś ją edytować , aby opisać, jak i dlaczego działa kod?
Florrie
4

Czy funkcje strzałek w ES6 naprawdę rozwiązują „to” w JavaScript

Powyższe łącze wyjaśnia, że ​​funkcje strzałek thisnie zmieniają się wraz z bind, call, applyfunkcjami.

Wyjaśnia to bardzo ładny przykład.

uruchom to, node v4aby zobaczyć „oczekiwane” zachowanie,

this.test = "attached to the module";
var foo = { test: "attached to an object" };
foo.method = function(name, cb){ 
    // bind the value of "this" on the method 
    // to try and force it to be what you want 
    this[name] = cb.bind(this); };
foo.method("bar", () => { console.log(this.test); });
foo.bar(); 
Prithvi Raj Vuppalapati
źródło
Prośba o podanie bardziej odpowiednich informacji w samej odpowiedzi, może to być przykładowy kod lub zredagowane wersje pytań
Jithin Scaria
// uruchom to w węźle v4, aby zobaczyć "oczekiwane" zachowanie this.test = "dołączone do modułu"; var foo = {test: "dołączony do obiektu"}; foo.method = function (name, cb) {// przypisz wartość "this" w metodzie //, aby spróbować wymusić to, co chcesz this [name] = cb.bind (this); }; foo.method ("bar", () => {console.log (this.test);}); foo.bar ();
Prithvi Raj Vuppalapati
Co ciekawe, spróbuj tego, a następnie spróbuj ponownie, zastępując foo.method ('bar', () => {console.log (this.test);}); with foo.method ('bar', function () {console.log (this.test);}); - pierwsza wersja logów "dołączona do modułu" a druga "załączona do obiektu" - naprawdę wolę bardziej stabilne traktowanie "tego" za pomocą funkcji strzałkowych. Nadal możesz osiągnąć inne efekty przy użyciu różnych wzorców, a wyniki są bardziej czytelne i przewidywalne IMO.
Methodician
3

To samo pytanie zadałem kilka dni temu.

Nie można powiązać wartości, ponieważ thisjest już powiązana .

Powiązanie innego tego zakresu z operatorem funkcji ES6 =>

fos.alex
źródło
4
"ponieważ to jest już thispowiązane " - W zwykłych funkcjach też jest powiązany. Chodzi o to, że w funkcjach strzałkowych nie ma lokalnego powiązania.
lepszy oliver
2

Krótko mówiąc, NIE MOŻESZ przypisać funkcji strzałek, ale czytaj dalej:

Wyobraź sobie, że masz tę funkcję strzałki poniżej, która thiswyświetla na konsoli:

const myFunc = ()=> console.log(this);

Szybkim rozwiązaniem byłoby użycie normalnej funkcji, więc po prostu zmień to na:

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

Następnie możesz powiązać go z dowolnym środowiskiem leksykalnym za pomocą bindlub calllub apply:

const bindedFunc = myFunc.bind(this);

i zadzwoń w przypadku bind.

bindedFunc();

Istnieją również sposoby, aby eval()to zrobić, co zdecydowanie nie jest zalecane .

Alireza
źródło
function myFuncjest behawioralnie inny poza możliwością wiązania; bliższe dopasowanie byłoby const myFunc = function() {...}. Ciekawi mnie również, co masz na myśli, używając eval, ponieważ jest to podejście, o którym nie sądzę, aby jakaś odpowiedź tu wcześniej udostępniła - ciekawie byłoby zobaczyć, jak to się robi, a następnie przeczytać, dlaczego jest to tak mocno odradzane.
Florrie
1

Może ten przykład ci pomoże:

let bob = {
   _name: "Bob",
   _friends: ["stackoverflow"],
   printFriends:(x)=> {
      x._friends.forEach((f)=> {
         console.log(x._name + " knows " + f);
      });
   }
}

bob.printFriends = (bob.printFriends).bind(null,bob);
bob.printFriends();

Ehsan
źródło
gøød stuff ... lekko intérésting
Aleks