Как мне написать именованную функцию стрелки в 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
Необязательно, функции стрелок поддерживают лексическую область видимости, поэтому было бы весьма полезно иметь сокращение для создания именованных функций (полезно для трассировки стека) с лексической областью действия
— Matt Styles

6
Что не так со вторым фрагментом?
— Берги

Вы наверняка можете ссылаться на назначенное имя внутри тела функции: var sayHello = (name) => {console.log (sayHello); }; И в случае рекурсивных функций, вы часто будете делать именно это. Не очень полезный пример, но вот функция, которая возвращает себя, если не получает своего аргумента: var sayHello = (name) => name? Console.log (name + 'говорит привет'): sayHello; SayHello () ( 'откровенного'); // -> «
— Фрэнк здоровается

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

Ответы:


211

Как мне написать именованную функцию стрелки в ES2015?

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

По спецификации у этой функции есть настоящее имя sayHello:

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

Это определено в Операторы присваивания> Семантика времени выполнения: оценка, где она вызывает абстрактную SetFunctionNameоперацию (этот вызов в настоящее время находится на шаге 1.e.iii).

Аналогично, семантика времени выполнения: 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 is: foo
ошибка
    в foo (http://stacksnippets.net/js:14:23)
    на http://stacksnippets.net/js:17:3

Обратите внимание на 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:3

Обратите внимание , что имя отсутствует foo.name is:, но это будет показано в трассировке стека. Просто на самом деле реализация name свойства функции имела более низкий приоритет, чем некоторые другие функции ES2015; Chrome и Firefox теперь есть; У Эджа есть это позади флага, вероятно, это не будет позади флага намного дольше.

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

Верный. Не существует синтаксиса объявления функций для функций стрелок, только синтаксис выражений функций , и нет стрелки, эквивалентной имени в названном выражении функции старого стиля ( 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

Похоже, что с помощьюact-native на некоторых устройствах по-прежнему не возвращается имя или реагирует, а реагирующий-нативный / реагирует-нативный отладчик не подбирает имя
— Zorgatone

86

Нет. Синтаксис стрелки является сокращением для анонимных функций. Анонимные функции, ну, в общем, анонимные.

Именованные функции определяются с помощью functionключевого слова.


16
Как заявил @ DenysSéguret, не является сокращением для анонимных функций . Вы получите жестко привязанную функцию. Вы можете увидеть полученный результат здесь bit.do/es2015-arrow, чтобы лучше понять, что это означает ...
— sminutoli

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

4
@ DenysSéguret: Вы можете просто назвать их, это в спецификации. Вы делаете это так, как в вопросе исключен OP, который дает функции (а не только переменной) истинное имя.
— TJ Crowder

5
@TJCrowder Спасибо за эту информацию и за ваш полезный ответ . Я не знал эту (очень и очень странную) особенность.
— Денис Сегюре

4
Этот ответ не правильный. На вопрос правильно ответил @TJCrowder ниже
— Дренаи

44

Если под именем named вы имеете в виду, что хотите, чтобы .nameсвойство функции стрелки было установлено, вам повезло.

Если функция стрелки определена в правой части выражения присваивания, движок возьмет имя слева и использует его для установки функций стрелки .name, например

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

sayHello.name //=== 'sayHello'

Сказав это, ваш вопрос, кажется, больше «могу ли я получить функцию стрелки, чтобы поднять?». Боюсь, ответом на этот вопрос будет большое «нет».


1
В текущей версии chrome, по крайней мере, 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, наконец, будет). Подробности . Но ОП специально исключил это (причудливо).
— Ти Джей Краудер

Могу ли я получить это имя в 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 stage 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. Подробности .
— Ти Джей Краудер

0

На самом деле, похоже, что один из способов назвать функции стрелок (по крайней мере, в chrome 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
— Девин Дж Род

-1

чтобы написать именованную функцию стрелки, вы можете привести нижеприведенный пример, где у меня есть класс с именем LoginClass, и внутри этого класса я написал стрелку с именем function , названную successAuth class LoginClass {

    constructor() {

    }

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

    }

}

1
Всегда лучше добавить объяснение к коду, который вы публикуете в качестве ответа, чтобы посетители могли понять, почему это хороший ответ.
— abarisone

Это делает то, что ОП сказал, что он не хочет делать, и полагается на это предложение, которое только на 2-м этапе и, вероятно, даже не собирается делать ES2017 (но я думаю, что это, скорее всего, ES2018, и транспортеры поддержали его месяцами). Он также эффективно дублирует несколько предыдущих ответов, что бесполезно.
— TJ Crowder

-2

ЭТО ЭС6

Да, я думаю, что вы ищете что-то вроде этого:

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.