Як записати названу функцію стрілки в 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.