Kiedy powinienem używać funkcji strzałek w ECMAScript 6?


406

Pytanie skierowane jest do osób, które zastanawiały się nad stylem kodu w kontekście nadchodzącego ECMAScript 6 (Harmony) i które już pracowały z tym językiem.

Za pomocą () => {}i function () {}otrzymujemy dwa bardzo podobne sposoby pisania funkcji w ES6. W innych językach funkcje lambda często wyróżniają się anonimowością, ale w ECMAScript każda funkcja może być anonimowa. Każdy z tych dwóch typów ma unikalne domeny użytkowania (a mianowicie kiedy thismusi być albo wyraźnie związany, albo jawnie nie związany). Pomiędzy tymi domenami istnieje ogromna liczba przypadków, w których wystarczy jedna z notacji.

Funkcje strzałek w ES6 mają co najmniej dwa ograniczenia:

  • Nie pracuj z newi nie można go używać podczas tworzeniaprototype
  • Naprawiono thisograniczenie zakresu podczas inicjalizacji

Pomijając te dwa ograniczenia, funkcje strzałek mogłyby teoretycznie zastąpić zwykłe funkcje prawie wszędzie. Jakie jest właściwe podejście z wykorzystaniem ich w praktyce? Czy należy używać funkcji strzałek, np .:

  • „wszędzie, gdzie działają”, tzn. wszędzie funkcja nie musi być agnostyczna wobec thiszmiennej i nie tworzymy obiektu.
  • tylko „wszędzie tam, gdzie są potrzebne”, tj. detektory zdarzeń, przekroczenia limitu czasu, które muszą być ograniczone do określonego zakresu
  • z funkcjami „krótkimi”, ale nie z funkcjami „długimi”
  • tylko z funkcjami, które nie zawierają innej funkcji strzałki

To, czego szukam, jest wskazówką dotyczącą wyboru odpowiedniej notacji funkcji w przyszłej wersji ECMAScript. Wytyczne będą musiały być jasne, aby można było ich nauczyć programistów w zespole, i muszą być spójne, aby nie wymagały ciągłego refaktoryzacji tam iz powrotem od jednej notacji funkcji do drugiej.


33
Uważasz za Fixed this bound to scope at initialisationograniczenie?
thefourtheye

12
Jest to zaleta, ale może być również ograniczeniem, jeśli planujesz ponownie użyć funkcji poza oryginalnym kontekstem. Na przykład podczas dynamicznego dodawania funkcji do klasy za pomocą Object.prototype. „Ograniczenie” rozumiem przez to, że zmianę wartości thismożna wykonać za pomocą zwykłych funkcji, ale nie za pomocą funkcji strzałek.
lizosening

1
Szczerze mówiąc, myślę, że wytyczne dotyczące stylu kodowania są raczej opiniotwórcze. Nie zrozumcie mnie źle, myślę, że są ważne, ale nie ma jednej wytycznej, która byłaby odpowiednia dla wszystkich.
Felix Kling

Nie uważam tego Fixed this bound to scope at initialisationza ograniczenie. :) Spójrz na ten artykuł: exploringjs.com/es6/ch_arrow-functions.html
NgaNguyenDuy

3
@thefourtheye, „ograniczenie” oznacza tutaj „ograniczenie, ponieważ głupi automatyczny tłumacz kodu nie mógł na ślepo zamieniać się nawzajem i zakładać, że wszystko będzie działało zgodnie z oczekiwaniami”.
Pacerier

Odpowiedzi:


322

Jakiś czas temu nasz zespół przeprowadził migrację całego kodu (średniej wielkości aplikacji AngularJS) do JavaScript skompilowanego przy użyciu Traceur Babel . Teraz używam następującej ogólnej zasady dla funkcji w ES6 i późniejszych:

  • Użyj functionw zakresie globalnym i dla Object.prototypewłaściwości.
  • Użyj classdla konstruktorów obiektów.
  • Używaj =>wszędzie indziej.

Po co używać funkcji strzałek prawie wszędzie?

  1. Bezpieczeństwo zakresu: Gdy funkcje strzałek są używane konsekwentnie, gwarantuje się, że wszystko będzie działać thisObjecttak samo jak root. Jeśli nawet jedno standardowe wywołanie zwrotne funkcji zostanie zmieszane z kilkoma funkcjami strzałek, istnieje szansa, że ​​zakres zostanie pomieszany.
  2. Kompaktowość: Funkcje strzałek są łatwiejsze do odczytu i zapisu. (Może się to wydawać uzasadnione, dlatego podam kilka przykładów).
  3. Jasność: gdy prawie wszystko jest funkcją strzałki, każdy regularny functionnatychmiast wyróżnia się w określaniu zakresu. Deweloper może zawsze sprawdzić następną wyższą functioninstrukcję, aby zobaczyć, co to thisObjectjest.

Dlaczego zawsze używamy regularnych funkcji w zakresie globalnym lub zakresie modułu?

  1. Aby wskazać funkcję, która nie powinna uzyskać dostępu do thisObject.
  2. windowPrzedmiot (zakres globalny) jest najlepiej skierowana wyraźnie.
  3. Wiele Object.prototypedefinicji żyje w zasięgu globalnym (pomyśl String.prototype.truncateitd.), A te i tak muszą być typu function. Konsekwentne stosowanie functionw zakresie globalnym pomaga uniknąć błędów.
  4. Wiele funkcji w zakresie globalnym to konstruktory obiektów dla definicji klas w starym stylu.
  5. Funkcje można nazwać 1 . Ma to dwie zalety: (1) Pisanie jest mniej niewygodne function foo(){}niżconst foo = () => {} - w szczególności poza innymi wywołaniami funkcji. (2) Nazwa funkcji jest wyświetlana w śladach stosu. Nazwanie każdego wewnętrznego wywołania zwrotnego byłoby nudne, jednak nazwanie wszystkich funkcji publicznych jest prawdopodobnie dobrym pomysłem.
  6. Deklaracje funkcji są podnoszone (co oznacza, że ​​można uzyskać do nich dostęp zanim zostaną zadeklarowane), co jest użytecznym atrybutem w statycznej funkcji użyteczności.


Konstruktory obiektów

Próba utworzenia instancji funkcji strzałki generuje wyjątek:

var x = () => {};
new x(); // TypeError: x is not a constructor

Jedną z kluczowych zalet funkcji nad funkcjami strzałek jest zatem to, że działają one podwójnie jako konstruktory obiektów:

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

Jednak funkcjonalnie identyczna definicja klasy roboczej 2 ES Harmony jest prawie tak kompaktowa:

class Person {
    constructor(name) {
        this.name = name;
    }
}

Oczekuję, że stosowanie poprzedniej notacji ostatecznie zostanie zniechęcone. Notacja konstruktora obiektów może być nadal używana przez niektóre proste fabryki anonimowych obiektów, w których obiekty są generowane programowo, ale niewiele więcej.

Tam, gdzie potrzebny jest konstruktor obiektów, należy rozważyć zamianę funkcji na classpokazaną powyżej. Składnia działa również z anonimowymi funkcjami / klasami.


Czytelność funkcji strzałek

Prawdopodobnie najlepszym argumentem za trzymaniem się funkcji regularnych - do diabła z bezpieczeństwem zakresu - byłoby to, że funkcje strzałek są mniej czytelne niż funkcje zwykłe. Jeśli kod nie działa przede wszystkim, funkcje strzałek mogą nie wydawać się konieczne, a gdy funkcje strzałek nie są używane konsekwentnie, wyglądają brzydko.

ECMAScript zmienił się nieco, odkąd ECMAScript 5.1 dał nam funkcjonalność Array.forEach, Array.mapa wszystkie te funkcje programowania funkcjonalnego, które pozwalają nam korzystać z funkcji, w których wcześniej używane byłyby pętle for. Asynchroniczny JavaScript znacznie wystartował. ES6 wyśle ​​również Promiseobiekt, co oznacza jeszcze więcej anonimowych funkcji. Nie ma powrotu do programowania funkcjonalnego. W funkcjonalnym JavaScript funkcje strzałek są lepsze niż zwykłe funkcje.

Weźmy na przykład ten (szczególnie mylący) fragment kodu 3 :

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(articles => Promise.all(articles.map(article => article.comments.getList())))
        .then(commentLists => commentLists.reduce((a, b) => a.concat(b)));
        .then(comments => {
            this.comments = comments;
        })
}

Ten sam fragment kodu z normalnymi funkcjami:

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(function (articles) {
            return Promise.all(articles.map(function (article) { 
                return article.comments.getList();
            }));
        })
        .then(function (commentLists) {
            return commentLists.reduce(function (a, b) {
                return a.concat(b); 
            });
        })
        .then(function (comments) {
            this.comments = comments;
        }.bind(this));
}

Podczas gdy dowolną z funkcji strzałek można zastąpić funkcją standardową, niewiele można na tym zyskać. Która wersja jest bardziej czytelna? Powiedziałbym pierwszy.

Myślę, że z czasem pytanie, czy używać funkcji strzałek, czy funkcji regularnych, będzie mniej istotne. Większość funkcji albo stanie się metodami klasowymi, które usuwają functionsłowo kluczowe, albo staną się klasami. Funkcje będą nadal używane do łatania klas poprzez Object.prototype. Tymczasem sugeruję zarezerwowanie functionsłowa kluczowego na wszystko, co naprawdę powinno być metodą klasową lub klasą.


Notatki

  1. Nazwane funkcje strzałek zostały odroczone w specyfikacji ES6 . Mogą być nadal dodawane w przyszłej wersji.
  2. Zgodnie ze szkicem specyfikacji „Deklaracje / wyrażenia klas tworzą konstruktor parę funkcji / prototypu dokładnie tak, jak dla deklaracji funkcji”, o ile klasa nie używa extendsłowa kluczowego. Niewielka różnica polega na tym, że deklaracje klas są stałymi, podczas gdy deklaracje funkcji nie.
  3. Uwaga na temat bloków w funkcjach strzałek z pojedynczą instrukcją: Lubię używać bloku wszędzie tam, gdzie wywoływana jest funkcja strzałki dla samego efektu ubocznego (np. Przypisanie). W ten sposób jasne jest, że wartość zwracaną można odrzucić.

4
Innym razem, gdy chcesz skorzystać, functionjest to, że nie chcesz thisbyć związany, prawda? Moim najczęstszym scenariuszem są zdarzenia, w których możesz chcieć thisodwoływać się do obiektu (zwykle węzła DOM), który wywołał zdarzenie.
Brett,

13
Właściwie myślę, że w przykładzie 3 zwykłe funkcje są bardziej czytelne. Nawet osoby niebędące programistami mogą zrozumieć, co się dzieje. Za pomocą strzałek musisz dokładnie wiedzieć, jak działają, aby zrozumieć ten przykład. Może więcej znaków nowej linii pomogłoby w przykładzie ze strzałkami, ale nie wiem. Tylko moje 2 centy, ale strzały powodują, że się kulę (ale jeszcze ich nie użyłem, więc wkrótce się
Spencer,

3
@Spencer to uczciwa kwestia. Z własnego doświadczenia =>wynika, że ​​z czasem wyglądam lepiej. Wątpię, aby nie-programiści bardzo inaczej postrzegali te dwa przykłady. Jeśli piszesz kod ES2016, zwykle nie będziesz używać tak wielu funkcji strzałek. W tym przykładzie, używając asynchronizacji / oczekiwania i interpretacji tablic, uzyskasz tylko jedną funkcję strzałki w reduce()wywołaniu.
lizosening

3
Całkowicie zgadzam się ze Spencerem, że regularne funkcje są znacznie bardziej czytelne w tym przykładzie.
jonschlinkert,

2
Dobra odpowiedź, dzięki! osobiście w miarę możliwości używam również strzałek w zakresie globalnym. Nie pozostawia mi to prawie żadnej „funkcji”. Dla mnie „funkcja” w kodzie oznacza szczególny przypadek, który musi wystać i być uważnie przemyślany.
kofifus

80

Zgodnie z propozycją , strzały miały na celu „rozwiązanie i rozwiązanie kilku typowych problemów związanych z tradycyjnym Function Expression”. Zamierzali poprawić sprawy, wiążąc thisleksykalnie i oferując zwięzłą składnię.

Jednak,

  • Nie można konsekwentnie wiązać thisleksykalnie
  • Składnia funkcji strzałek jest delikatna i niejednoznaczna

Dlatego funkcje strzałek stwarzają możliwość pomyłek i błędów i należy je wyłączyć ze słownika programisty JavaScript, zastępując je functionwyłącznie.

Odnośnie leksykalnej this

this jest problematyczne:

function Book(settings) {
    this.settings = settings;
    this.pages = this.createPages();
}
Book.prototype.render = function () {
    this.pages.forEach(function (page) {
        page.draw(this.settings);
    }, this);
};

Funkcje strzałek mają rozwiązać problem polegający na tym, że musimy uzyskać dostęp do właściwości thiswewnątrz wywołania zwrotnego. Jest już na to kilka sposobów: Można przypisać thisdo zmiennej, użyć bindlub użyć trzeciego argumentu dostępnego w Arraymetodach agregujących. Jednak strzałki wydają się być najprostszym obejściem, więc metodę można by zmienić w następujący sposób:

this.pages.forEach(page => page.draw(this.settings));

Zastanów się jednak, czy kod używał biblioteki takiej jak jQuery, której metody wiążą się thisspecjalnie. Teraz thismamy do czynienia z dwiema wartościami:

Book.prototype.render = function () {
    var book = this;
    this.$pages.each(function (index) {
        var $page = $(this);
        book.draw(book.currentPage + index, $page);
    });
};

Musimy używać function, eachaby wiązać się thisdynamicznie. Nie możemy tutaj użyć funkcji strzałki.

Radzenie sobie z wieloma thiswartościami może być również mylące, ponieważ trudno jest ustalić, o którym thisautor mówił:

function Reader() {
    this.book.on('change', function () {
        this.reformat();
    });
}

Czy autor rzeczywiście chciał zadzwonić Book.prototype.reformat? A może zapomniał związać thisi zamierzał zadzwonić Reader.prototype.reformat? Jeśli zmienimy moduł obsługi na funkcję strzałki, podobnie będziemy się zastanawiać, czy autor chciał dynamiki this, ale wybrał strzałkę, ponieważ pasuje do jednej linii:

function Reader() {
    this.book.on('change', () => this.reformat());
}

Można postawić: „Czy to wyjątkowe, że strzałki mogą być czasem niewłaściwą funkcją? Być może, jeśli rzadko potrzebujemy thiswartości dynamicznych , to nadal byłoby dobrze używać strzałek przez większość czasu”.

Ale zadaj sobie następujące pytanie: „Czy warto byłoby debugować kod i stwierdzić, że wynikiem błędu był„ przypadek na krawędzi ”?” Wolałbym unikać problemów nie tylko przez większość czasu, ale także 100% czasu.

Jest lepszy sposób: Zawsze używaj function(więc thiszawsze może być dynamicznie związany) i zawsze odwołuj się thisprzez zmienną. Zmienne są leksykalne i przyjmują wiele nazw. Przypisanie thisdo zmiennej wyjaśni twoje intencje:

function Reader() {
    var reader = this;
    reader.book.on('change', function () {
        var book = this;
        book.reformat();
        reader.reformat();
    });
}

Co więcej, zawsze przypisywanie thisdo zmiennej (nawet gdy istnieje jedna thislub żadna inna funkcja) zapewnia, że ​​intencje pozostają jasne nawet po zmianie kodu.

Ponadto dynamika thisnie jest niczym wyjątkowym. jQuery jest używany na ponad 50 milionach stron internetowych (od tego pisania w lutym 2016 r.). Oto inne interfejsy API wiążące się thisdynamicznie:

  • Mocha (wczoraj ~ 120 tys. Pobrań) udostępnia metody testów za pośrednictwem this.
  • Grunt (wczoraj ~ 63 tys. Pobrań) udostępnia metody tworzenia zadań za pośrednictwem this.
  • Backbone (wczoraj ~ 22 000 pobrań) określa metody dostępu this.
  • API Imprez (jak Doma) odnoszą się do ugrupowania EventTargetz this.
  • Prototypowe interfejsy API, które są załatane lub rozszerzone, odnoszą się do instancji z this.

(Statystyki za pośrednictwem http://trends.builtwith.com/javascript/jQuery i https://www.npmjs.com .)

Prawdopodobnie już potrzebujesz thispowiązań dynamicznych .

thisCzasem oczekuje się leksyki , ale czasem nie; tak jak thisczasami oczekuje się dynamiki , ale czasem nie. Na szczęście istnieje lepszy sposób, który zawsze wytwarza i komunikuje oczekiwane wiązanie.

Odnośnie skróconej składni

Funkcje strzałek zapewniły „krótszą formę składniową” dla funkcji. Ale czy te krótsze funkcje sprawią, że odniesiesz większy sukces?

Czy x => x * x„łatwiej jest czytać” niż function (x) { return x * x; }? Być może jest tak, ponieważ bardziej prawdopodobne jest wygenerowanie jednego, krótkiego wiersza kodu. Według Dysona Wpływ prędkości odczytu i długości linii na skuteczność odczytu z ekranu ,

Wydaje się, że średnia długość linii (55 znaków na linię) wspiera efektywny odczyt przy normalnej i dużej prędkości. Spowodowało to najwyższy poziom zrozumienia. . .

Podobne uzasadnienia dotyczą operatora warunkowego (trójskładnikowego) i ifinstrukcji jednowierszowych .

Czy naprawdę piszesz proste funkcje matematyczne reklamowane we wniosku ? Moje domeny nie są matematyczne, więc moje podprogramy rzadko są tak eleganckie. Często widzę, że funkcje strzałek przekraczają limit kolumn i zawijają się do innej linii ze względu na edytor lub przewodnik po stylu, który niweluje „czytelność” według definicji Dysona.

Można by postawić: „A może po prostu użyć krótkiej wersji do krótkich funkcji, jeśli to możliwe?” Ale teraz reguła stylistyczna stoi w sprzeczności z ograniczeniami językowymi: „Spróbuj użyć możliwie najkrótszej notacji funkcji, pamiętając, że czasami tylko najdłuższa notacja wiąże się thiszgodnie z oczekiwaniami”. Takie pomieszanie sprawia, że ​​strzały są szczególnie podatne na niewłaściwe użycie.

Istnieje wiele problemów ze składnią funkcji strzałek:

const a = x =>
    doSomething(x);

const b = x =>
    doSomething(x);
    doSomethingElse(x);

Obie te funkcje są poprawne pod względem składniowym. Ale doSomethingElse(x);nie ma go w treści b, jest to po prostu słabo wcięte oświadczenie najwyższego poziomu.

Po rozwinięciu do postaci bloku nie ma już domniemania return, którego przywrócenie można zapomnieć. Ale to wyrażenie mogło mieć jedynie na celu wywołanie efektu ubocznego, więc kto wie, czy wyraźne returnbędzie konieczne w przyszłości?

const create = () => User.create();

const create = () => {
    let user;
    User.create().then(result => {
        user = result;
        return sendEmail();
    }).then(() => user);
};

const create = () => {
    let user;
    return User.create().then(result => {
        user = result;
        return sendEmail();
    }).then(() => user);
};

To, co może być przeznaczone jako parametr spoczynkowy, można przeanalizować jako operator rozkładania:

processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest

Przypisanie można pomylić z domyślnymi argumentami:

const a = 1;
let x;
const b = x => {}; // No default
const b = x = a => {}; // "Adding a default" instead creates a double assignment
const b = (x = a) => {}; // Remember to add parens

Bloki wyglądają jak obiekty:

(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object

Co to znaczy?

() => {}

Czy autor miał zamiar utworzyć brak operacji lub funkcję zwracającą pusty obiekt? (Mając to na uwadze, powinniśmy zawsze umieszczać {po =>? Powinniśmy ograniczyć się tylko do składni wyrażeń? To by jeszcze bardziej zmniejszyć częstotliwość strzałkami.)

=>wygląda <=i >=:

x => 1 ? 2 : 3
x <= 1 ? 2 : 3

if (x => 1) {}
if (x >= 1) {}

Aby natychmiast wywołać wyrażenie funkcji strzałki, należy umieścić je ()na zewnątrz, ale umieszczenie ()w środku jest prawidłowe i może być zamierzone.

(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function

Chociaż, jeśli ktoś pisze (() => doSomething()());z zamiarem napisania natychmiast wywołanego wyrażenia funkcyjnego, po prostu nic się nie wydarzy.

Trudno argumentować, że funkcje strzałek są „bardziej zrozumiałe”, biorąc pod uwagę wszystkie powyższe przypadki. Jeden mógł poznać wszystkie szczególne zasady niezbędne do wykorzystania tej składni. Czy to naprawdę jest tego warte?

Składnia functionjest wyjątkowo uogólniona. Używanie functionwyłącznie oznacza, że ​​sam język zapobiega pisaniu mylącego kodu. Aby napisać procedury, które powinny być rozumiane syntaktycznie we wszystkich przypadkach, wybieram function.

Jeśli chodzi o wytyczne

Prosisz o wytyczne, które muszą być „jasne” i „spójne”. Użycie funkcji strzałek ostatecznie spowoduje powstanie poprawnego pod względem składniowym, logicznie niepoprawnego kodu, z obiema formami funkcji splecionymi, sensownie i arbitralnie. Dlatego oferuję:

Wytyczne dotyczące notacji funkcji w ES6:

  • Zawsze twórz procedury za pomocą function.
  • Zawsze przypisuj thisdo zmiennej. Nie używać () => {}.

5
Interesujące napisanie o widoku funkcjonalnego programisty na JavaScript. Nie jestem pewien, czy zgadzam się z argumentem zmiennych prywatnych. IMO niewielu ludzi naprawdę ich potrzebuje; ci, którzy to zrobią, prawdopodobnie będą również potrzebować innych funkcji kontraktowych i wybiorą rozszerzenie języka takie jak TypeScript. Z pewnością widzę apelację selfzamiast tego. Podane pułapki funkcji strzałek również są ważne i obowiązują tu te same standardy, co w przypadku innych instrukcji, które mogą być stosowane bez nawiasów klamrowych; w przeciwnym razie myślę, że z twoim argumentem równie dobrze można promować wszędzie funkcje strzałek.
lizanie,

7
„Posiadanie wielu sposobów robienia rzeczy stwarza niepotrzebne wektory argumentów i niezgody w miejscu pracy i społeczności językowej. Byłoby lepiej, gdyby gramatyka języka nie pozwalała nam dokonywać złych wyborów”. Zgadzam się bardzo. Niezły opis! Myślę, że funkcje strzałek są w rzeczywistości krokiem wstecz. Na inny temat chciałbym, aby moi współpracownicy przestali próbować przekształcić JavaScript w język C # za pomocą serii definicji .prototype. To jest obrzydliwe. Powinienem anonimowo połączyć twój post :)
Spencer

11
Bardzo dobrze napisane! Chociaż nie zgadzam się z większością twoich punktów, ważne jest, aby rozważyć przeciwny punkt widzenia.
minexew

4
Nie funkcje strzałek, ale dziwne zachowanie thisjest problemem Javascript. Zamiast być niejawnie związany, thispowinien zostać przekazany jako jawny argument.
Bob

5
Zawsze używaj funkcji (dzięki czemu zawsze można ją dynamicznie powiązać) i zawsze odwołuj się do niej za pomocą zmiennej. ”. Nie mogłem się więcej nie zgodzić!

48

Funkcje strzałek zostały stworzone w celu uproszczenia funkcji scopei rozwiązania thissłowa kluczowego poprzez uproszczenie go. Wykorzystują =>składnię, która wygląda jak strzała.

Uwaga: Nie zastępuje istniejących funkcji. Jeśli zastąpisz każdą składnię funkcji funkcjami strzałek, nie będzie ona działać we wszystkich przypadkach.

Spójrzmy na istniejącą składnię ES5. Gdyby thissłowo kluczowe znajdowało się w metodzie obiektu (funkcja należąca do obiektu), do czego by się odnosiło?

var Actor = {
  name: 'RajiniKanth',
  getName: function() {
     console.log(this.name);
  }
};
Actor.getName();

Powyższy fragment odnosi się do objecti wypisuje nazwę "RajiniKanth". Przyjrzyjmy się poniższemu fragmentowi i zobaczmy, o co tu chodzi.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach(function(movie) {
     alert(this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

A co jeśli thissłowo kluczowe było w środku method’s function?

Tutaj to będzie dotyczyć window objectniż inner functionjak jej wypadł scope. Ponieważ thiszawsze odwołuje się do właściciela funkcji, w której się znajduje, w tym przypadku - ponieważ nie jest już objęty zakresem - obiektu okna / obiektu globalnego.

Gdy jest w objectmetodzie - functionwłaścicielem jest obiekt. Zatem to słowo kluczowe jest powiązane z obiektem. Jednak gdy znajduje się wewnątrz funkcji, albo samodzielnie, albo w innej metodzie, zawsze będzie odnosić się do window/globalobiektu.

var fn = function(){
  alert(this);
}

fn(); // [object Window]

Istnieją sposoby rozwiązania tego problemu w nas ES5samych, przyjrzyjmy się temu przed zanurzeniem się w funkcjach strzałek ES6, w jaki sposób je rozwiązać.

Zazwyczaj tworzysz zmienną poza funkcją wewnętrzną metody. Teraz ‘forEach’zyskuje metoda dostępu do this, a tym samym na object’swłaściwości i ich wartości.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   var _this = this;
   this.movies.forEach(function(movie) {
     alert(_this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

za pomocą, bindaby dołączyć thissłowo kluczowe odnoszące się do metody do method’s inner function.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach(function(movie) {
     alert(this.name + " has acted in " + movie);
   }.bind(this));
  }
};

Actor.showMovies();

Teraz dzięki ES6funkcji strzałki możemy uporać się z lexical scopingproblemem w prostszy sposób.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach((movie) => {
     alert(this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

Arrow functionssą bardziej podobne do instrukcji funkcyjnych, z tą różnicą, że bindto parent scope. Jeżeli arrow function is in top scope, thisargumentem będzie dotyczyć window/global scope, natomiast funkcja strzałka wewnątrz regularnie funkcji będą mieć ten argument taki sam jak jego zewnętrznej funkcji.

Z arrowfunkcjami thisjest związany scopew momencie tworzenia i nie można go zmienić. Nowy operator, powiązanie, wywołanie i zastosowanie nie mają na to wpływu.

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
  callback(param);
  }, 1);
};

// With a traditional function if we don't control
// the context then can we lose control of `this`.
var o = {
  doSomething: function () {
  // Here we pass `o` into the async function,
  // expecting it back as `param`
  asyncFunction(o, function (param) {
  // We made a mistake of thinking `this` is
  // the instance of `o`.
  console.log('param === this?', param === this);
  });
  }
};

o.doSomething(); // param === this? false

W powyższym przykładzie straciliśmy nad tym kontrolę. Możemy rozwiązać powyższy przykład, używając zmiennej odwołania thislub używając bind. Z ES6, staje się łatwiejszy w zarządzaniu this, jak jego związany lexical scoping.

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
  callback(param);
  }, 1);
};

var o = {
  doSomething: function () {
  // Here we pass `o` into the async function,
  // expecting it back as `param`.
  //
  // Because this arrow function is created within
  // the scope of `doSomething` it is bound to this
  // lexical scope.
  asyncFunction(o, (param) => {
  console.log('param === this?', param === this);
  });
  }
};

o.doSomething(); // param === this? true

Kiedy nie do funkcji strzałek

Wewnątrz obiektu dosłownie.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  getName: () => {
     alert(this.name);
  }
};

Actor.getName();

Actor.getNamejest zdefiniowany za pomocą funkcji strzałki, ale przy wywołaniu alarmuje niezdefiniowany, ponieważ this.namejest undefinedtak, jak pozostaje kontekst window.

Dzieje się tak, ponieważ funkcja strzałki wiąże kontekst leksykalnie z window object... tzn. Zasięgiem zewnętrznym. Wykonywanie this.namejest równoważne z window.name, co jest niezdefiniowane.

Prototyp obiektu

Ta sama zasada obowiązuje przy definiowaniu metod na prototype object. Zamiast używać funkcji strzałki do definiowania metody sayCatName, która powoduje niepoprawne context window:

function Actor(name) {
  this.name = name;
}
Actor.prototype.getName = () => {
  console.log(this === window); // => true
  return this.name;
};
var act = new Actor('RajiniKanth');
act.getName(); // => undefined

Wywoływanie konstruktorów

thisw wywołaniu konstrukcji jest nowo utworzony obiekt. Podczas wykonywania nowej Fn (), kontekst constructor Fnjest nowy obiekt: this instanceof Fn === true.

this jest konfigurowany z otaczającego kontekstu, tj. zewnętrznego zasięgu, który powoduje, że nie jest on przypisywany do nowo utworzonego obiektu.

var Message = (text) => {
  this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');

Oddzwonienie z kontekstem dynamicznym

Funkcja strzałki wiąże contextstatycznie przy deklaracji i nie jest możliwe uczynienie go dynamicznym. Dołączanie detektorów zdarzeń do elementów DOM jest częstym zadaniem w programowaniu po stronie klienta. Zdarzenie wyzwala funkcję modułu obsługi z tym jako elementem docelowym.

var button = document.getElementById('myButton');
button.addEventListener('click', () => {
  console.log(this === window); // => true
  this.innerHTML = 'Clicked button';
});

thisto okno w funkcji strzałki zdefiniowane w kontekście globalnym. Gdy nastąpi zdarzenie kliknięcia, przeglądarka próbuje wywołać funkcję modułu obsługi z kontekstem przycisku, ale funkcja strzałki nie zmienia wcześniej zdefiniowanego kontekstu. this.innerHTMLjest równoważne window.innerHTMLi nie ma sensu.

Musisz zastosować wyrażenie funkcyjne, które pozwala to zmienić w zależności od elementu docelowego:

var button = document.getElementById('myButton');
button.addEventListener('click', function() {
  console.log(this === button); // => true
  this.innerHTML = 'Clicked button';
});

Gdy użytkownik kliknie przycisk, w funkcji modułu obsługi jest to przycisk. W ten sposób this.innerHTML = 'Clicked button'poprawnie modyfikuje tekst przycisku, aby odzwierciedlić status kliknięcia.

Odnośniki: https://dmitripavlutin.com/when-not-to-use-arrow-functions-in-javascript/


Cóż, muszę przyznać, że „najlepsze leży w środku” . Upoważnione do stwierdzenia, że ​​funkcje strzałek nie obejmują żadnych możliwych przypadków użycia funkcji. Są naprawdę zaprojektowane, aby rozwiązać tylko część typowych problemów. Wystarczy przejść na nie całkowicie, to będzie przesada.
BlitZ

@DmitriPavlutin: Sprawdź mój zaktualizowany post, to zbiór wielu rzeczy ... być może powinienem opublikować referencję.
Thalaivar

2
Twój kod po wierszu „za pomocą binda dołącza to słowo kluczowe, które odnosi się do metody do funkcji wewnętrznej metody”. zawiera błędy. Czy przetestowałeś pozostałe przykłady?
Izaak Pak

Ten using bind to attach the this keyword that refers to the method to the method’s inner function.ma błędy składniowe.
Coda Chang

Powinno byćvar Actor = { name: 'RajiniKanth', movies: ['Kabali', 'Sivaji', 'Baba'], showMovies: function() { this.movies.forEach(function(movie){ alert(this.name + ' has acted in ' + movie); }.bind(this)) } }; Actor.showMovies();
Coda Chang

14

Funkcje strzałek - jak dotąd najczęściej używana funkcja ES6 ...

Użycie: Wszystkie funkcje ES5 należy zastąpić funkcjami strzałek ES6, z wyjątkiem następujących scenariuszy:

NIE należy używać funkcji strzałek:

  1. Kiedy chcemy funkcji podnoszenia
    • ponieważ funkcje strzałek są anonimowe.
  2. Kiedy chcemy użyć this/ argumentsw funkcji
    • ponieważ funkcje strzałek nie mają this/ argumentszależą od zewnętrznego kontekstu.
  3. Kiedy chcemy użyć funkcji o nazwie
    • ponieważ funkcje strzałek są anonimowe.
  4. Kiedy chcemy użyć funkcji jako constructor
    • ponieważ funkcje strzałek nie mają własnych this.
  5. Kiedy chcemy dodać funkcję jako właściwość do literału obiektu i użyć w nim obiektu
    • ponieważ nie możemy uzyskać dostępu this(co powinno być samym obiektem).

Pozwól nam zrozumieć niektóre warianty funkcji strzałek, aby lepiej zrozumieć:

Wariant 1 : Kiedy chcemy przekazać więcej niż jeden argument do funkcji i zwrócić z niej pewną wartość.

Wersja ES5 :

var multiply = function (a,b) {
    return a*b;
};
console.log(multiply(5,6)); //30

Wersja ES6 :

var multiplyArrow = (a,b) => a*b;
console.log(multiplyArrow(5,6)); //30

Uwaga: functionsłowo kluczowe NIE jest wymagane. =>jest wymagane. {}są opcjonalne, jeśli nie podamy, {} returnjest domyślnie dodany przez JavaScript, a gdy podamy, {}musimy dodać, returnjeśli jest to potrzebne.

Wariant 2 : Gdy chcemy przekazać TYLKO jeden argument do funkcji i zwrócić z niej pewną wartość.

Wersja ES5 :

var double = function(a) {
    return a*2;
};
console.log(double(2)); //4

Wersja ES6 :

var doubleArrow  = a => a*2;
console.log(doubleArrow(2)); //4

Uwaga: przekazując tylko jeden argument, możemy pominąć nawias ().

Wariant 3 : Kiedy NIE chcemy przekazać żadnego argumentu do funkcji i NIE chcemy zwrócić żadnej wartości.

Wersja ES5 :

var sayHello = function() {
    console.log("Hello");
};
sayHello(); //Hello

Wersja ES6 :

var sayHelloArrow = () => {console.log("sayHelloArrow");}
sayHelloArrow(); //sayHelloArrow

Wariant 4 : Kiedy chcemy jawnie powrócić z funkcji strzałek.

Wersja ES6 :

var increment = x => {
  return x + 1;
};
console.log(increment(1)); //2

Wariant 5 : Kiedy chcemy zwrócić obiekt z funkcji strzałek.

Wersja ES6 :

var returnObject = () => ({a:5});
console.log(returnObject());

Uwaga: Musimy owinąć obiekt w nawiasach, w ()przeciwnym razie JavaScript nie będzie w stanie odróżnić bloku od obiektu.

Wariant 6 : Funkcje strzałek NIE mają argumentswłasnych obiektów (tablicowych), zależą od zewnętrznego kontekstu arguments.

Wersja ES6 :

function foo() {
  var abc = i => arguments[0];
  console.log(abc(1));
};    
foo(2); // 2

Uwaga: foojest funkcją ES5, z argumentstablicy jak przedmiotu i przekazany do argumentu jest 2więc arguments[0]do foo2.

abcjest funkcja ES6 strzałka ponieważ nie ma własne argumentsstąd drukuje arguments[0]z fooTo zewnętrznej kontekście zamiast.

Wariant 7 : Funkcje strzałek NIE mają thiswłasnych, zależą od zewnętrznego kontekstuthis

Wersja ES5 :

var obj5 = {
  greet: "Hi, Welcome ",
  greetUser : function(user) {
        setTimeout(function(){
        console.log(this.greet + ": " +  user); // "this" here is undefined.
        });
     }
};

obj5.greetUser("Katty"); //undefined: Katty

Uwaga: Wywołanie zwrotne przekazane do setTimeout jest funkcją ES5 i ma swoją własną, thisktóra nie jest zdefiniowana w use-strictśrodowisku, dlatego otrzymujemy dane wyjściowe:

undefined: Katty

Wersja ES6 :

var obj6 = {
  greet: "Hi, Welcome ",
  greetUser : function(user) {
    setTimeout(() => console.log(this.greet + ": " +  user)); 
      // this here refers to outer context
   }
};

obj6.greetUser("Katty"); //Hi, Welcome: Katty

Uwaga: Przekazane wywołanie zwrotne setTimeoutjest funkcją strzałki ES6 i NIE ma własnej, thiswięc bierze ją z zewnętrznego kontekstu, greetUserktóry ma this, obj6dlatego otrzymujemy dane wyjściowe:

Hi, Welcome: Katty

Różne: Nie możemy używać newz funkcjami strzałek. Funkcje strzałek nie mają prototypewłaściwości. NIE mamy powiązania, thiskiedy funkcja strzałki jest wywoływana przez applylub call.


6

Oprócz świetnych dotychczasowych odpowiedzi chciałbym przedstawić zupełnie inny powód, dla którego funkcje strzałek są w pewnym sensie zasadniczo lepsze niż „zwykłe” funkcje JavaScript. Dla celów dyskusji załóżmy tymczasowo, że używamy sprawdzania typów, takiego jak TypeScript lub „Flow” Facebooka. Rozważ następujący moduł zabawki, który jest prawidłowym kodem ECMAScript 6 oraz adnotacjami typu Flow: (Na końcu tej odpowiedzi dołączę kod bez typu, który realistycznie powstałby z Babel, aby można go było uruchomić).

export class C {
  n : number;
  f1: number => number; 
  f2: number => number;

  constructor(){
    this.n = 42;
    this.f1 = (x:number) => x + this.n;
    this.f2 = function (x:number) { return  x + this.n;};
  }
}

Zobaczmy teraz, co się stanie, gdy użyjemy klasy C z innego modułu, na przykład:

let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1: number = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2: number = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!

Jak widzisz, sprawdzanie typów nie powiodło się tutaj: f2 miał zwrócić liczbę, ale zwrócił ciąg!

Gorzej, wydaje się, że nie można sobie wyobrazić sprawdzania typu nie obsługuje zwykłych funkcji JavaScript (bez strzałek), ponieważ „to” z f2 nie występuje na liście argumentów f2, więc nie można dodać wymaganego typu „to” jako adnotacja do f2.

Czy ten problem dotyczy także osób, które nie używają sprawdzania typów? Myślę, że tak, ponieważ nawet jeśli nie mamy żadnych typów statycznych, myślimy, jakby tam były. („Pierwsze parametry muszą być liczbą, drugie ciągiem znaków itp.) Ukryty„ ten ”instrument, który może, ale nie musi być użyty w ciele funkcji, utrudnia naszą mentalną księgowość.

Oto uruchamialna, nietypowa wersja, którą wyprodukowałby Babel:

class C {
    constructor() {
        this.n = 42;
        this.f1 = x => x + this.n;
        this.f2 = function (x) { return x + this.n; };
    }
}

let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1 = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2 = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!



3

Nadal trzymam się wszystkiego, co napisałem w pierwszej odpowiedzi w tym wątku. Jednak moja opinia na temat stylu kodu rozwinęła się od tego czasu, więc mam nową odpowiedź na to pytanie, która opiera się na moim ostatnim.

Odnośnie leksykalnej this

W ostatniej odpowiedzi umyślnie porzuciłem przekonanie, które mam na temat tego języka, ponieważ nie było to bezpośrednio związane z argumentem, który przedstawiłem. Niemniej jednak, bez wyraźnego stwierdzenia tego, rozumiem, dlaczego wiele osób po prostu nie zgadza się z moim zaleceniem, aby nie używać strzał, gdy uznają je za przydatne.

Wierzę w to: nie powinniśmy używać thisw pierwszej kolejności. Dlatego jeśli ktoś celowo unika używania thisw swoim kodzie, wówczas „leksykalna this” funkcja strzałek ma niewielką lub żadną wartość. Ponadto, przy założeniu, że thisjest to coś złego, traktowanie strzały thisjest mniej „dobrem”; zamiast tego jest bardziej formą kontroli obrażeń dla innej funkcji w złym języku.

Wydaje mi się, że albo nie zdarza się to niektórym ludziom, ale nawet tym, którym to robi, muszą oni zawsze znajdować się w bazach kodowych, w których thisWydaje pojawia się sto razy na plik, a wszystko to (lub dużo) kontroli szkód jest wszystkim rozsądna osoba może mieć nadzieję. Tak więc strzały mogą być dobre, jeśli poprawią złą sytuację.

Nawet jeśli łatwiej jest pisać kod ze thisstrzałkami niż bez nich, zasady używania strzałek pozostają bardzo złożone (patrz: aktualny wątek). Zatem wytyczne nie są ani „jasne”, ani „spójne”, tak jak prosiłeś. Nawet jeśli programiści wiedzą o dwuznacznościach strzał, myślę, że i tak wzruszają ramionami i akceptują je, ponieważ wartość leksykalna thisprzesłania je.

Wszystko to jest wstępem do następującej realizacji: jeśli się nie używa this, wówczas dwuznaczność dotycząca thistych strzałek zwykle staje się nieistotna. Strzały stają się w tym kontekście bardziej neutralne.

Odnośnie skróconej składni

Kiedy napisałem swoją pierwszą odpowiedź, byłem zdania, że ​​nawet niewolnicze stosowanie się do najlepszych praktyk było wartościową ceną do zapłacenia, jeśli oznaczałoby to, że mogłem stworzyć doskonalszy kod. Ale w końcu zdałem sobie sprawę, że zwięzłość może służyć jako forma abstrakcji, która może również poprawić jakość kodu - na tyle, aby uzasadniać czasem rezygnację z najlepszych praktyk.

Innymi słowy: cholera, chcę też funkcji one-liner!

Jeśli chodzi o wytyczne

Z możliwością thisużycia neutralnych funkcji strzałek i zwięzłości, które warto realizować, oferuję następujące łagodniejsze wytyczne:

Wytyczne dotyczące notacji funkcji w ES6:

  • Nie używać this.
  • Użyj deklaracji funkcji dla funkcji, które wywołujesz według nazwy (ponieważ są one podnoszone).
  • Użyj funkcji strzałek do wywołań zwrotnych (ponieważ są one zwykle bardziej zwięzłe).

Zgadzam się w 100% z sekcją „Wytyczne dotyczące notacji funkcji w ES6” na dole - szczególnie z funkcjami podnoszenia i wbudowanymi funkcjami zwrotnymi. niezła odpowiedź!
Jeff McCloud

1

W prosty sposób

var a =20; function a(){this.a=10; console.log(a);} 
//20, since the context here is window.

Kolejna instancja:

var a = 20;
function ex(){
this.a = 10;
function inner(){
console.log(this.a); //can you guess the output of this line.
}
inner();
}
var test = new ex();

Odp .: Konsola wydrukuje 20.

Powodem jest to, że za każdym razem, gdy funkcja jest wykonywana, tworzony jest własny stos, w tym przykładzie exfunkcja jest wykonywana z newoperatorem, więc kontekst zostanie utworzony, a po inneruruchomieniu JS utworzy nowy stos i wykona innerfunkcjęglobal context chociaż istnieje kontekst lokalny.

Tak więc, jeśli chcemy, aby innerfunkcja miała kontekst lokalny, to exznaczy musimy powiązać kontekst z funkcją wewnętrzną.

Strzały rozwiązują ten problem, zamiast brać Global contextje, local contextjeśli istnieją. W given example,to zajmie new ex()jak this.

Tak więc we wszystkich przypadkach, w których wiązanie jest jawne Strzałki domyślnie rozwiązują problem.


1

Funkcje strzałek lub Lambdas zostały wprowadzone w ES 6. Oprócz elegancji w minimalnej składni, najbardziej zauważalną różnicą funkcjonalną jest określenie zakresu this funkcji strzałki

W wyrażeniach funkcji regularnychthis słowo kluczowe jest powiązane z różnymi wartościami w zależności od kontekstu, w którym jest wywoływane.

W funkcji strzałek , thisjest leksykalnie związany, co oznacza, że zamyka się thisod zakresu, w którym została zdefiniowana funkcja strzałka (rodzic-zakres) i nie zmienia się bez względu na to gdzie i jak jest ona wywoływana / nazywa.

Ograniczenia Funkcje strzałek jako metody na obiekcie

// this = global Window
let objA = {
 id: 10,
 name: "Simar",
 print () { // same as print: function() 
  console.log(`[${this.id} -> ${this.name}]`);
 }
}
objA.print(); // logs: [10 -> Simar]
objA = {
 id: 10,
 name: "Simar",
 print: () => {
  // closes over this lexically (global Window)
  console.log(`[${this.id} -> ${this.name}]`);
 }
};
objA.print(); // logs: [undefined -> undefined]

W przypadku, objA.print()gdy print()metoda została zdefiniowana przy użyciu funkcji regularnej function , działała, rozwiązując thispoprawnie polecenie objAwywołania metody, ale nie powiodła się, gdy została zdefiniowana jako =>funkcja strzałki . Jest tak, ponieważ thisw zwykłej funkcji wywoływanej jako metoda na obiekcie ( objA) jest sam obiekt. Jednak w przypadku funkcji strzałki thiszostaje ona powiązana leksykalnie thisz zakresem obejmującym, w którym została zdefiniowana (w naszym przypadku globalne / Window) i pozostaje niezmieniona podczas wywoływania jako metody objA.

Zalety funkcji strzałek w porównaniu z funkcjami zwykłymi w metodzie obiektu ALE tylko wtedy, gdy thisoczekuje się, że zostanie ustalony i związany w definicji czasu.

/* this = global | Window (enclosing scope) */

let objB = {
 id: 20,
 name: "Paul",
 print () { // same as print: function() 
  setTimeout( function() {
    // invoked async, not bound to objB
    console.log(`[${this.id} -> ${this.name}]`);
  }, 1)
 }
};
objB.print(); // logs: [undefined -> undefined]'
objB = {
 id: 20,
 name: "Paul",
 print () { // same as print: function() 
  setTimeout( () => {
    // closes over bind to this from objB.print()
    console.log(`[${this.id} -> ${this.name}]`);
  }, 1)
 }
};
objB.print(); // logs: [20 -> Paul]

W przypadku objB.print(), gdy print()sposób jest zdefiniowany jako funkcja, która wywołuje console.log([$ {this.id} -> {this.name}] )asynchronicznie w postaci połączenia zwrotnego na setTimeout , thisrozwiązane prawidłowo objB, kiedy funkcja strzałka użyto wywołania zwrotnego, lecz nie kiedy oddzwonienie zostało zdefiniowane jako zwykła funkcja. Wynika to z faktu, że =>funkcja strzałki przekazana setTimeout(()=>..)została zamknięta thisleksykalnie od jej rodzica tj. wywoływanie objB.print()go zdefiniowało. Innymi słowy, =>funkcja strzałki została przekazana do setTimeout(()==>...przypisanej objBjako jej, thisponieważ wywołanie objB.print() thisbyło objBsamo.

Możemy łatwo użyć Function.prototype.bind(), aby wywołanie zwrotne zdefiniowane jako normalna funkcja działało, wiążąc je z poprawną this.

const objB = {
 id: 20,
 name: "Singh",
 print () { // same as print: function() 
  setTimeout( (function() {
    console.log(`[${this.id} -> ${this.name}]`);
  }).bind(this), 1)
 }
}
objB.print() // logs: [20 -> Singh]

Jednak funkcje strzałek przydają się i są mniej podatne na błędy w przypadku wywołań zwrotnych asynchronicznych, w których znamy thisw momencie definicji funkcji, do których się ona zwraca i powinna być ograniczona.

Ograniczenie funkcji strzałek, gdy musi się to zmieniać w różnych wywołaniach

W każdej chwili potrzebujemy funkcji, którą thismożna zmienić w momencie wywołania, nie możemy używać funkcji strzałek.

/* this = global | Window (enclosing scope) */

function print() { 
   console.log(`[${this.id} -> {this.name}]`);
}
const obj1 = {
 id: 10,
 name: "Simar",
 print // same as print: print
};
obj.print(); // logs: [10 -> Simar]
const obj2 = {
 id: 20,
 name: "Paul",
};
printObj2 = obj2.bind(obj2);
printObj2(); // logs: [20 -> Paul]
print.call(obj2); // logs: [20 -> Paul]

Żadne z powyższych nie będzie działać z funkcją strzałki const print = () => { console.log([$ {this.id} -> {this.name}], );}ponieważ thisnie można go zmienić i pozostanie związane thisz zakresem obejmującym, w którym zostało zdefiniowane (global / Window). We wszystkich tych przykładach wywołaliśmy tę samą funkcję z różnymi obiektami ( obj1i obj2) jeden po drugim, oba utworzone po print()zadeklarowaniu funkcji.

Były to wymyślone przykłady, ale zastanówmy się nad kilkoma prawdziwymi przykładami. Gdybyśmy musieli napisać naszą reduce()metodę podobną do tej, która działa arrays , ponownie nie możemy zdefiniować jej jako lambda, ponieważ musi ona wywnioskowaćthis z kontekstu wywołania, tj. tablica, na której została wywołana

Z tego powodu constructorfunkcje nigdy nie mogą być zdefiniowane jako funkcje strzałek, ponieważ thisdla funkcji konstruktora nie można ustawić w momencie deklaracji. Za każdym razem, gdy wywoływana jest funkcja konstruktoranew słowem kluczowym, tworzony jest nowy obiekt, który następnie zostaje powiązany z tym konkretnym wywołaniem.

Również gdy ramy lub systemy akceptują funkcje zwrotne, które mają być później wywoływane w kontekście dynamicznym this , nie możemy używać funkcji strzałek, ponieważ thismoże to wymagać zmiany przy każdym wywołaniu. Taka sytuacja często występuje w przypadku programów obsługi zdarzeń DOM

'use strict'
var button = document.getElementById('button');
button.addEventListener('click', function {
  // web-api invokes with this bound to current-target in DOM
  this.classList.toggle('on');
});
var button = document.getElementById('button');
button.addEventListener('click', () => {
  // TypeError; 'use strict' -> no global this
  this.classList.toggle('on');
});

Jest to również powód, dla którego w frameworkach takich jak Angular 2+ i Vue.js oczekuje się, że metody wiązania szablonu-komponentu będą regularnymi funkcjami / metodami, ponieważ thisich wywołanie jest zarządzane przez ramy dla funkcji wiązania. (Angular używa Zone.js do zarządzania kontekstem asynchronicznym do wywoływania funkcji wiązania szablonu widoku).

Z drugiej strony, w React , kiedy chcemy przekazać metodę komponentu jako procedurę obsługi zdarzeń, na przykład <input onChange={this.handleOnchange} />powinniśmy zdefiniować handleOnchanage = (event)=> {this.props.onInputChange(event.target.value);}jako funkcję strzałki, jak przy każdym wywołaniu, chcemy, aby była to ta sama instancja komponentu, który wygenerował JSX do renderowania Element DOM.


Ten artykuł jest również dostępny w mojej publikacji Medium . Jeśli podoba Ci się artile, lub masz jakiekolwiek uwagi i sugestie, proszę Clap lub pozostawić komentarze na medium .

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.