Как мне написать именованную функцию стрелки в 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 ?

jhamm
источник
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

Я не говорю, что это красиво, но это работает, если вам абсолютно и безусловно нужна единая оболочка для выражений.

TJ Crowder
источник
Как я могу предотвратить установку имени (как в старых двигателях)?
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'

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

hughfdjackson
источник
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();
scre_www
источник