Як записати названу функцію стрілки в ES2015?


204

У мене є функція, яку я намагаюся перетворити на новий синтаксис стрілки в ES6 . Це названа функція:

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

Чи є спосіб надати ім'я без заяви var:

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

Очевидно, я можу використовувати цю функцію лише після того, як я її визначив. Щось таке:

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

Чи є новий спосіб зробити це в ES6 ?


3
Чи не сенс синтаксису стрілки в тому, щоб не давати ім'я функції?
AstroCB

64
Не обов'язково функції стрілок підтримують лексичну область застосування, тому матиме скорочення для створення іменованих функцій (корисних для сліду стека) з лексичним розмахом, було б дуже корисно
Метт Стайлз

6
Що не так з другим фрагментом?
Бергі

Ви, безумовно, можете посилатися на призначене ім’я всередині тіла функції: var sayHello = (ім'я) => {console.log (sayHello); }; А у випадку рекурсивних функцій ви часто будете робити саме це. Не надто корисний приклад, але ось функція, яка повертається сама, якщо вона не отримує свій аргумент: var sayHello = (ім'я) => ім'я? Console.log (ім'я + 'каже привіт'): sayHello; sayHello () ("відвертий"); // -> "відвертий вітається"
Dtipson

4
Заголовок питання надзвичайно оманливий у порівнянні із його змістом, оскільки ви виключаєте спосіб назви функцій стрілок (що є другим фрагментом).
TJ Crowder

Відповіді:


211

Як записати названу функцію стрілки в ES2015?

Ви робите це так, як ви вирішили у своєму запитанні: ви розміщуєте його в правій частині завдання або ініціалізатора властивостей, де змінна або ім'я властивості може розумно використовуватись як ім'я двигуном JavaScript. Немає іншого способу зробити це, але зробити це правильно і повністю охоплено специфікацією.

За специфікацією ця функція має справжнє ім'я sayHello:

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

Це визначено в Операторах присвоєння> Семантика виконання: Оцінка, коли вона викликає абстрактну SetFunctionNameоперацію (цей виклик зараз знаходиться на етапі 1.e.iii).

Similiarly, Runtime Semantics: PropertyDefinitionEvaluation викликає SetFunctionNameі таким чином дає цій функції справжнє ім'я:

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

Сучасні двигуни встановлюють внутрішню назву функції для таких операторів; Edge все ще має біт, який робить його доступним, як nameна екземплярі функції за прапором виконання.

Наприклад, у Chrome або Firefox відкрийте веб-консоль і запустіть цей фрагмент:

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

У Chrome 51 і новіших версіях, Firefox 53 і вище (і на Edge 13 і вище з експериментальним прапором), коли ви запустите це, ви побачите:

foo.name: foo
Помилка
    в foo (http://stacksnippets.net/js:14:23)
    за адресою http://stacksnippets.net/js:17

Зверніть увагу на foo.name is: fooі Error...at foo.

У Chrome 50 і новіших версіях, Firefox 52 і новіших версіях, і Edge без експериментального прапора, ви побачите це замість цього, оскільки вони не мають Function#nameвластивості (поки):

foo.name: 
Помилка
    в foo (http://stacksnippets.net/js:14:23)
    за адресою http://stacksnippets.net/js:17

Зверніть увагу , що ім'я відсутнє foo.name is:, але це буде показано в трасуванні стека. Просто реальна реалізація name властивості функції мала нижчий пріоритет, ніж деякі інші функції ES2015; У Chrome і Firefox зараз є; Edge має його за прапором, імовірно, він не буде позаду прапора набагато довше.

Очевидно, я можу використовувати цю функцію лише після того, як я її визначив

Правильно. Не існує синтаксису оголошення функцій для функцій стрілок, лише синтаксис вираження функцій , і немає стрілки, еквівалентної імені, у старому стилі з іменем виразу функції ( var f = function foo() { };). Тож немає еквівалента:

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

Ви повинні розбити його на два вирази (я б заперечував, ви все одно повинні це робити ) :

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));

Звичайно, якщо вам потрібно поставити це там, де потрібен єдиний вираз, ви завжди можете ... використовувати функцію стрілки:

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

Я не кажу, що це досить, але це працює, якщо вам абсолютно позитивно потрібна обгортка з одним виразом.


Як запобігти встановленню імені (як у старих двигунах)?
trusktr

1
@trusktr: Я не можу зрозуміти, чому ви цього хочете, але якщо ви хочете запобігти цьому: Не робіть цього (-ів) вище. Наприклад, хоча let f = () => { /* do something */ };функція призначає ім'я, let g = (() => () => { /* do something */ })();це не робить.
TJ Crowder

Це я і закінчила. Я вважаю за краще анонімні класи, створені бібліотекою спадкування мого класу, щоб бути анонімними, ніж мати імена внутрішніх змінних, про які користувачеві моєї бібліотеки успадкування класу не потрібно думати, і я хотів би, щоб кінцевий користувач бачив ім'я лише якщо вони вказали назву для класу. ( github.com/trusktr/lowclass )
trusktr

4
@trusktr: Єдиний виняток, який вони зробили, може відповідати вашим цілям, то: Якщо ви призначите функцію властивості на існуючому об'єкті, ім'я не встановлено: o.foo = () => {};не дає функції імені. Це було навмисно для запобігання витоку інформації.
TJ Crowder

86

Ні. Синтаксис стрілки - це коротка форма для анонімних функцій. Анонімні функції, ну, анонімні.

Іменовані функції визначаються за допомогою functionключового слова.


16
Як заявив @ DenysSéguret, це не коротка форма для анонімних функцій . Ви отримаєте жорстку прив'язку функції. Ви можете побачити перекладений результат тут bit.do/es2015-arrow, щоб зрозуміти краще, що це означає ...
sminutoli

16
Перше слово цієї відповіді є правильним для заданого питання, оскільки ОП виключає спосіб назви функції стрілки. Але решта відповіді просто неправильна. Подивіться в специфікації, де використовується абстрактна SetFunctionNameоперація . let a = () => {};визначає названу функцію (назва - aяк за специфікацією, так і в сучасних двигунах (киньте в неї помилку, щоб побачити); вони просто ще не підтримують nameмайно (але це в специфікації, і вони з часом потраплять).
TJ Crowder

4
@ DenysSéguret: Ви можете просто назвати їх, це в специфікації. Ви робите це так, як ОР виключається у запитанні, що дає функції (а не лише змінній) справжнє ім'я.
TJ Crowder

5
@TJCrowder Дякую за цю інформацію та корисну відповідь . Я не знав цієї (дуже дуже дивної) особливості.
Denys Séguret

4
Ця відповідь не правильна. На питання правильно відповів @TJCrowder нижче
Drenai

44

Якщо під "ім'ям" ви маєте на увазі, що хочете встановити .nameвластивість функції стрілки, вам пощастить.

Якщо функція стрілки визначена в правій частині виразу призначення, двигун візьме ім’я з лівої сторони і використовуватиме його для встановлення функції стрілки .name, наприклад

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

sayHello.name //=== 'sayHello'

Сказавши це, ваше питання, здається, більше: "чи можу я отримати функцію стрілки на підйомник?". Боюся, що відповідь на це - великий «ні».


1
У поточній версії хрому, принаймні, sayHello.name все ще ""; Зауважте, що як і всі функції, sayHello є об'єктом, тому ви можете визначити довільні властивості для нього, якщо хочете. Ви не можете записати на "ім'я", як лише для читання, але ви можете сказатиHello.my_name = "sayHello"; Не впевнений, що з цим ви отримуєте, оскільки ви не можете зробити / посилатися на це в тій частині функції.
Dtipson

2
Я помилявся: поки ім’я не можна змінити безпосередньо, ви можете налаштувати його так: Object.defineProperty (sayHello, 'name', {value: 'sayHello'})
Dtipson

1
"Якщо в правій частині [..]" визначена функція стрілки, що є джерелом для цього? Я не можу знайти його в специфікації. Просто потрібна посилання на цитату.
Gajus

@Dtipson: Це лише тому, що сучасні двигуни ще не впровадили налаштування nameвластивості скрізь, коли специфікація ES2015 вимагає від них; вони доберуться до цього. Це одне з останніх речей у списку відповідності для Chrome, і навіть у Chrome 50 його немає (Chrome 51 нарешті буде). Деталі . Але ОП спеціально виключає робити це (химерно).
TJ Crowder

Чи можу я отримати це ім’я в toString? Я спробував її змінити, але тоді я не знаю, як отримати доступ до функції функції.
Qwerty

0

Здається, що це стане можливим за допомогою ES7: https://babeljs.io/blog/2015/06/07/react-on-es6-plus#arrow-functions

Наведений приклад:

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

Корпус функцій стрілок ES6 має таку саму лексичну, як і код, який їх оточує, що дає нам бажаний результат через спосіб визначення ініціалізаторів властивостей ES7.

Зауважте, що для роботи з babel мені потрібно було включити найбільш експериментальний синтаксис ES7 етап 0. У своєму файлі webpack.config.js я оновив навантажувач babel так:

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

6
Це точно не названа функція стрілки. Це ярлик для створення методів екземплярів у конструкторі, і мені цікаво, як це актуально тут?
Бергі

Привіт @Bergi, я не впевнений, чи це був задум оригінального автора, але я намагався створити іменовану функцію з такою ж областю, як і об'єкт. Якщо ми не використовуємо цю техніку, і хочемо мати відповідну область, ми повинні ввести її в конструктор класу. this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
Хаміш Керрі

8
Так, ви можете так само легко використовувати те, constructor() { this.handleOptionsButtonClick = (e) => {…}; }що абсолютно еквівалентно коду у вашій відповіді, але вже працює в ES6. Не потрібно користуватися .bind.
Бергі

1
До речі, я думаю, ви плутаєте "названу функцію" з методом "(екземпляр)" і "область" з " thisконтекстом"
Бергі

1
@tdecs: Так, можна; Йорг помиляється. let a = () => {};визначає названу функцію стрілки під назвою a. Деталі .
TJ Crowder

0

Насправді схоже, що одним із способів назви функцій стрілок (принаймні, щодо хрому 77 ...) є це:

"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);
}

введіть тут опис зображення


теоретично може створити Бабель макрос fn('yourName', ()=>{}), який може підтримувати 100% правильно стрілку функції семантики, або ще краще в Вавилонському плагіном для «імені синтаксису функції стрілки»: fn yourName() => {}. Будь-яке з них складеться до наведеного вище коду. Я думаю, що названі стрілки будуть такими BADA55
Devin G Rhode

-1

для того, щоб написати названу стрілочну функцію, ви можете скористатись наведеним нижче прикладом, де у мене є клас з ім'ям LoginClass, і всередині цього класу я написав функцію зі стрілкою з ім'ям , назване successAuth клас LoginClass {

    constructor() {

    }

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

    }

}

1
Завжди краще додати пояснення до коду, який ви публікуєте, як відповідь, щоб це допомогло відвідувачам зрозуміти, чому це хороша відповідь.
абарізон

Це робить те, що ОП заявив, що не хоче робити, і покладається на цю пропозицію, яка є лише на етапі 2, і, мабуть, навіть не збирається робити ES2017 (але я думаю, що це, швидше за все, складе ES2018, і транспілери підтримали це місяцями). Він також ефективно копіює кілька попередніх відповідей, що не корисно.
TJ Crowder

-2

ЦЕ Є ES6

Так, я думаю, що ти шукаєш щось таке:

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

Я спробував цей приклад, він працює чудово. Якась вагома причина для негативних голосів? Чи використовує це використання будь-які небажані побічні ефекти чи я пропускаю якийсь важливий момент? Ваша допомога в роз’ясненні моїх сумнівів буде вдячна.
FullMoon

@GaneshAdapa: Я сподіваюсь, що скорочення полягають у тому, що це говорить про те, що робити саме те, що в ОП сказав, що він / вона не хотів робити, не надаючи додаткової інформації.
TJ Crowder

-2

Ви можете пропустити функціональну частину та частину стрілки для створення функцій. Приклад:

 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();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.