Jak napisać nazwaną funkcję strzałki w ES2015?


204

Mam funkcję, którą próbuję przekonwertować na nową składnię strzałek w ES6 . Jest to nazwana funkcja:

function sayHello(name) {
    console.log(name + ' says hello');
}

Czy istnieje sposób, aby nadać mu nazwę bez instrukcji var:

var sayHello = (name) => {
    console.log(name + ' says hello');
}

Oczywiście mogę korzystać z tej funkcji tylko po jej zdefiniowaniu. Coś w stylu:

sayHello = (name) => {
        console.log(name + ' says hello');
    }

Czy jest nowy sposób na zrobienie tego w ES6 ?


3
Czy punktem składni strzałki nie jest nadanie funkcji nazwy?
AstroCB

64
Niekoniecznie, funkcje strzałek utrzymują zakres leksykalny, więc posiadanie skrótu do tworzenia nazwanych funkcji (przydatnych dla śledzenia stosu) z zasięgiem leksykalnym byłoby bardzo przydatne
Matt Styles

6
Co jest nie tak z drugim fragmentem?
Bergi,

Z pewnością możesz odwoływać się do przypisanej nazwy w treści funkcji: var sayHello = (name) => {console.log (sayHello); }; A w przypadku funkcji rekurencyjnych często to robisz. Niezbyt przydatny przykład, ale oto funkcja, która zwraca się, jeśli nie otrzyma swojego argumentu: var sayHello = (nazwa) => nazwa? Console.log (nazwa + „mówi cześć”): sayHello; sayHello () („frank”); // -> „szczery mówi cześć”
Dtipson

4
Tytuł pytania jest bardzo mylący w porównaniu do jego treści, ponieważ wykluczyłeś sposób nazywania funkcji strzałek (który jest drugim fragmentem).
TJ Crowder

Odpowiedzi:


211

Jak napisać nazwaną funkcję strzałki w ES2015?

Robisz to tak, jak wykluczyłeś swoje pytanie: Umieszczasz go po prawej stronie inicjatora przypisania lub właściwości, gdzie zmienna lub nazwa właściwości może być słusznie używana jako nazwa przez silnik JavaScript. Nie ma innego sposobu, aby to zrobić, ale jest to prawidłowe i całkowicie objęte specyfikacją.

Zgodnie ze specyfikacją ta funkcja ma prawdziwą nazwę sayHello:

var sayHello = name => {
    console.log(name + ' says hello');
};

Jest to zdefiniowane w Operatory przypisania> Runtime Semantics: Ocena, w której wywołuje operację abstrakcyjnąSetFunctionName (to wywołanie jest obecnie w kroku 1.e.iii).

Podobnie Runtime Semantics: PropertyDefinitionEvaluation wywołuje, SetFunctionNamea tym samym nadaje tej funkcji prawdziwą nazwę:

let o = {
    sayHello: name => {
        console.log(`${name} says hello`);
    }
};

Nowoczesne silniki już ustawiają wewnętrzną nazwę funkcji dla takich instrukcji; Edge wciąż ma bit, dzięki czemu jest dostępny jak namew instancji funkcji za flagą środowiska wykonawczego.

Na przykład w Chrome lub Firefox otwórz konsolę internetową, a następnie uruchom ten fragment kodu:

"use strict";
let foo = () => { throw new Error(); };
console.log("foo.name is: " + foo.name);
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

W Chrome 51 i nowszych oraz Firefox 53 i nowszych (oraz Edge 13 i nowszych z flagą eksperymentalną) po uruchomieniu zobaczysz:

foo.name to: foo
Błąd
    w foo (http://stacksnippets.net/js:14:23)
    na http://stacksnippets.net/js:17:3

Zwróć uwagę na foo.name is: fooi Error...at foo.

W Chrome 50 i wcześniejszych, Firefox 52 i wcześniejszych oraz Edge bez flagi eksperymentalnej, zobaczysz to zamiast tego, ponieważ nie mają jeszcze tej Function#namewłaściwości:

foo.name to: 
Błąd
    w foo (http://stacksnippets.net/js:14:23)
    na http://stacksnippets.net/js:17:3

Zauważ, że brakuje nazwy foo.name is:, ale jest ona wyświetlana w śladzie stosu. Tyle, że faktyczne wdrożenie name właściwości w funkcji miało niższy priorytet niż niektóre inne funkcje ES2015; Chrome i Firefox mają to teraz; Edge ma go za flagą, prawdopodobnie nie będzie już dłużej za flagą.

Oczywiście mogę korzystać z tej funkcji tylko po jej zdefiniowaniu

Poprawny. Nie ma składni deklaracji funkcji dla funkcji strzałek, tylko składnia wyrażeń funkcyjnych i nie ma strzałki równoważnej nazwie w nazwanym wyrażeniu funkcyjnym w starym stylu ( var f = function foo() { };). Więc nie ma odpowiednika:

console.log(function fact(n) {
    if (n < 0) {
        throw new Error("Not defined for negative numbers");
    }
    return n == 0 ? 1 : n * fact(n - 1);
}(5)); // 120

Musisz podzielić to na dwa wyrażenia (twierdzę, że i tak powinieneś to zrobić ) :

let fact = n => {
    if (n < 0) {
      throw new Error("Not defined for negative numbers.");
    }
    return n == 0 ? 1 : n * fact(n - 1);
};
console.log(fact(5));

Oczywiście, jeśli mają umieścić to gdzie wymagana jest pojedynczym wyrazem, zawsze można skorzystać z funkcji ... strzałki:

console.log((() => {
    let fact = n => {
        if (n < 0) {
            throw new Error("Not defined for negative numbers.");
        }
        return n == 0 ? 1 : n * fact(n - 1);
    };
    return fact(5);
})()); // 120

Nie mówię, że to ładne, ale działa, jeśli absolutnie, pozytywnie potrzebujesz opakowania pojedynczego wyrażenia.


Jak zapobiec ustawianiu nazwy (jak w starszych silnikach)?
trusktr

1
@trusktr: Nie rozumiem, dlaczego chcesz, ale jeśli chcesz temu zapobiec: nie rób rzeczy powyżej. Na przykład, chociaż let f = () => { /* do something */ };przypisuje nazwę do funkcji, let g = (() => () => { /* do something */ })();nie.
TJ Crowder

Tak właśnie skończyłem. Wolałbym, aby anonimowe klasy generowane przez moją bibliotekę dziedziczenia klas były anonimowe, niż mieć nazwy zmiennych wewnętrznych, o których użytkownik mojej biblioteki dziedziczenia klas nie musiałby myśleć, i chciałbym, aby użytkownik końcowy zobaczył nazwę tylko jeśli podali nazwę klasy. ( github.com/trusktr/lowclass )
trusktr

4
@trusktr: Jedyny wyjątek, który wprowadzili, może być odpowiedni dla twoich celów, a następnie: Jeśli przypiszesz funkcję do właściwości istniejącego obiektu, nazwa nie zostanie ustawiona: o.foo = () => {};nie nada funkcji nazwy. Było to celowe, aby zapobiec wyciekom informacji.
TJ Crowder

86

Nie. Składnia strzałki jest skrótem dla funkcji anonimowych. Funkcje anonimowe są, cóż, anonimowe.

Nazwane funkcje są zdefiniowane za pomocą functionsłowa kluczowego.


16
Jak stwierdził @ DenysSéguret, nie jest skrótem do anonimowych funkcji . Otrzymasz funkcję sztywno powiązaną . Możesz zobaczyć transpilowany wynik tutaj bit.do/es2015-arrow, aby lepiej zrozumieć, co to oznacza ...
sminutoli

16
Pierwsze słowo tej odpowiedzi jest poprawne dla zadanego pytania, ponieważ OP wykluczył sposób nazwania funkcji strzałki. Ale pozostała część odpowiedzi jest po prostu niepoprawna. Sprawdź w specyfikacji, gdzie używana jest operacja abstrakcyjnaSetFunctionName . let a = () => {};definiuje nazwaną funkcję (nazwa jest a) zarówno zgodnie ze specyfikacją, jak i we współczesnych silnikach (rzuć w nią błąd, aby zobaczyć); po prostu jeszcze nie obsługują tej namewłaściwości (ale jest w specyfikacji i w końcu tam dotrą).
TJ Crowder

4
@ DenysSéguret: Możesz je po prostu nazwać, to jest w specyfikacji. Robisz to w sposób wykluczony przez OP w pytaniu, co nadaje funkcji (nie tylko zmiennej) prawdziwą nazwę.
TJ Crowder,

5
@TJCrowder Dzięki za ten kawałek informacji i za przydatną odpowiedź . Nie znałem tej (bardzo dziwnej) funkcji.
Denys Séguret,

4
Ta odpowiedź jest nieprawidłowa. Pytanie odpowiedział poprawnie @TJCrowder poniżej
Drenajów

44

Jeśli przez „nazwany” masz na myśli, że chcesz .nameustawić właściwość funkcji strzałki, masz szczęście.

Jeśli funkcja strzałki jest zdefiniowana po prawej stronie wyrażenia przypisania, silnik pobierze nazwę po lewej stronie i użyje jej do ustawienia funkcji strzałki .name, np.

var sayHello = (name) => {
    console.log(name + ' says hello');
}

sayHello.name //=== 'sayHello'

Powiedziawszy to, twoje pytanie wydaje się bardziej „czy mogę uzyskać funkcję strzałki do podnoszenia?”. Obawiam się, że odpowiedź na to wielkie „nie”.


1
W obecnej wersji Chrome przynajmniej powiedzmy Hello.name wciąż „”; Zauważ, że podobnie jak wszystkie funkcje, sayHello jest obiektem, więc możesz zdefiniować dowolne właściwości, jeśli chcesz. Nie możesz pisać do „name”, ponieważ jest on tylko do odczytu, ale możesz powiedzieć: Hello.my_name = „sayHello”; Nie jestem jednak pewien, co Cię to dostanie, ponieważ nie możesz tego zrobić / odwołać się do treści funkcji.
Dtipson

2
Myliłem się: chociaż nazwa nie jest bezpośrednio modyfikowalna, możesz ją skonfigurować w następujący sposób: Object.defineProperty (sayHello, 'name', {value: 'sayHello'})
Dtipson 15.01.16

1
„Jeśli funkcja strzałki jest zdefiniowana po prawej stronie [..]”, jakie jest tego źródło? Nie mogę tego znaleźć w specyfikacji. Potrzebuję tylko odniesienia do wyceny.
Gajus

@Dtipson: To tylko dlatego, że nowoczesne silniki nie zaimplementowały jeszcze ustawiania namewłaściwości wszędzie tam, gdzie wymaga tego specyfikacja ES2015; dojdą do tego. Jest to jedna z ostatnich rzeczy na liście zgodności dla Chrome, a nawet Chrome 50 go nie ma (Chrome 51 w końcu to zrobi). Szczegóły . Ale OP specjalnie wykluczył to (dziwnie).
TJ Crowder

Czy mogę uzyskać tę nazwę w toString? Próbowałem to zmienić, ale nie wiem, jak uzyskać dostęp do treści funkcji.
Qwerty

0

Wygląda na to, że będzie to możliwe dzięki ES7: https://babeljs.io/blog/2015/06/07/react-on-es6-plus#arrow-functions

Podany przykład to:

class PostInfo extends React.Component {
  handleOptionsButtonClick = (e) => {
    this.setState({showOptionsModal: true});
  }
}

Treść funkcji strzałek ES6 ma tę samą leksykę, co otaczający je kod, co daje nam pożądany wynik ze względu na sposób, w jaki zakres inicjalizatorów właściwości ES7 jest ograniczony.

Zauważ, że aby to działało z babelem, musiałem włączyć najbardziej eksperymentalną składnię ES7 na etapie 0. W moim pliku webpack.config.js zaktualizowałem moduł ładujący babel w następujący sposób:

{test: /\.js$/, exclude: /node_modules/, loader: 'babel?stage=0'},

6
To nie jest dokładnie nazwana funkcja strzałki. To jest skrót do tworzenia metod instancji w konstruktorze i zastanawiam się, jak to jest tutaj istotne?
Bergi,

Cześć @ Bergi, nie jestem pewien, czy taki był zamiar oryginalnego autora, ale próbowałem utworzyć nazwaną funkcję o tym samym zakresie co obiekt. Jeśli nie użyjemy tej techniki i chcemy mieć odpowiedni zakres, musimy wprowadzić ją do konstruktora klasy. this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
Hamish Currie,

8
Możesz równie łatwo użyć, constructor() { this.handleOptionsButtonClick = (e) => {…}; }co jest całkowicie równoważne kodowi w odpowiedzi, ale działa już w ES6. Nie musisz używać .bind.
Bergi,

1
Btw, myślę, że mylisz „nazwaną funkcję” z „metodą (instancji)” i „zasięg” z „ thiskontekstem”
Bergi

1
@tdecs: Tak, możesz; Jörg się myli. let a = () => {};definiuje nazwaną funkcję strzałki o nazwie a. Szczegóły .
TJ Crowder

0

Wygląda na to, że jednym ze sposobów nazwania funkcji strzałek (przynajmniej od Chrome 77 ...) jest:

"use strict";
let fn_names = {};
fn_names.foo = () => { throw new Error(); };
console.log("foo.name is: " + foo.name);
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

wprowadź opis zdjęcia tutaj


teoretycznie może utworzyć makro babel fn('yourName', ()=>{}), który mógłby zapewnić 100% semantykę funkcyjnych poprawne strzałek, albo jeszcze lepiej wtyczki babel dla „nazwanego składni funkcji strzałka”: fn yourName() => {}. Każdy z nich skompilowałby się do powyższego kodu. Myślę, że nazwane strzały byłyby tak BADA55
Devin G Rhode

-1

aby napisać nazwaną funkcję strzałki, możesz znaleźć się na poniższym przykładzie, w którym mam klasę o nazwie LoginClass, a wewnątrz tej klasy napisałem funkcję o nazwie strzałki o nazwie successAuth klasa LoginClass {

    constructor() {

    }

    successAuth = (dataArgs)=> { //named arow function

    }

}

1
Zawsze lepiej jest dodać wyjaśnienie do kodu, który publikujesz jako odpowiedź, aby pomóc odwiedzającym zrozumieć, dlaczego jest to dobra odpowiedź.
abarisone

Robi to, o czym powiedział PO, że nie chce tego robić, i opiera się na tej propozycji, która jest dopiero na etapie 2 i prawdopodobnie nawet nie stworzy ES2017 (ale myślę, że prawdopodobnie stworzy ES2018, a transpilatorzy to poparli przez miesiące). Skutecznie powiela również kilka poprzednich odpowiedzi, co nie jest przydatne.
TJ Crowder

-2

TO JEST ES6

Tak, myślę, że szukasz czegoś takiego:

const foo = (depth) => {console.log("hi i'm Adele")}
foo -> // the function itself
foo() -> // "hi i'm Adele"

Próbowałem tego przykładu, działa dobrze. Czy jest jakiś dobry powód do oddawania głosów negatywnych? Czy to użycie powoduje niepożądane efekty uboczne, czy brakuje mi ważnego punktu? Twoja pomoc w wyjaśnieniu moich wątpliwości zostanie doceniona.
FullMoon

@GaneshAdapa: Oczekuję, że opinie są negatywne, ponieważ sugeruje to robienie dokładnie tego, co OP powiedział, że nie chce robić, bez podawania dalszych informacji.
TJ Crowder

-2

Możesz pominąć część funkcji i część strzałki, aby utworzyć funkcje. Przykład:

 class YourClassNameHere{

   constructor(age) {
     this.age = age;
   }

   foo() {
     return "This is a function with name Foo";
   }

   bar() {
     return "This is a function with name bar";
   }

 }

let myVar = new YourClassNameHere(50);
myVar.foo();
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.