Como escrevo uma função de seta nomeada no ES2015?

204

Eu tenho uma função que estou tentando converter para a nova sintaxe de seta em ES6 . É uma função nomeada:

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

Existe uma maneira de dar um nome a ele sem uma instrução var:

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

Obviamente, só posso usar essa função depois de defini-la. Algo como o seguinte:

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

Existe uma nova maneira de fazer isso no ES6 ?

jhamm
fonte
3
O ponto da sintaxe da seta não é dar um nome à função?
AstroCB
64
Não necessariamente, seta funções manter escopo léxico assim que ter um atalho para produzir funções nomeadas (útil para um rastreamento de pilha) com escopo léxico seria muito útil
Styles Matt
6
O que há de errado com o segundo trecho?
Bergi 08/07/2015
Você certamente pode fazer referência a um nome atribuído dentro do corpo da função: var sayHello = (name) => {console.log (sayHello); }; E no caso de funções recursivas, muitas vezes você faz exatamente isso. Não é um exemplo super útil, mas aqui está uma função que retorna automaticamente se não obtiver seu argumento: var sayHello = (nome) => nome? Console.log (nome + 'diz olá'): sayHello; sayHello () ('franco'); // -> "frank diz olá"
Dtipson 14/01/16
4
O título da pergunta é extremamente enganador em comparação com o conteúdo, porque você descartou a maneira como nomeia as funções de seta (que é o segundo trecho).
TJ Crowder

Respostas:

211

Como escrevo uma função de seta nomeada no ES2015?

Você faz isso da maneira que descartou em sua pergunta: você o coloca no lado direito de uma atribuição ou inicializador de propriedades em que a variável ou o nome da propriedade pode ser razoavelmente usado como um nome pelo mecanismo JavaScript. Não há outra maneira de fazer isso, mas fazer isso é correto e totalmente coberto pela especificação.

Por especificação, essa função tem um nome verdadeiro sayHello:

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

Isso é definido em Operadores de Designação> Semântica de Tempo de Execução: Avaliação, onde chama a SetFunctionNameoperação abstrata (essa chamada está atualmente na etapa 1.e.iii).

Da mesma forma, o Runtime Semantics: PropertyDefinitionEvaluation chama SetFunctionNamee, portanto, atribui a essa função um nome verdadeiro:

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

Os mecanismos modernos definem o nome interno da função para declarações como essa; O Edge ainda tem o bit disponível, como namena instância da função atrás de um sinalizador de tempo de execução.

Por exemplo, no Chrome ou Firefox, abra o console da Web e execute este trecho:

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

No Chrome 51 e acima e Firefox 53 e acima (e Edge 13 e acima com um sinalizador experimental), quando você o executa, você verá:

foo.name é: foo
Erro
    no foo (http://stacksnippets.net/js:14:23)
    em http://stacksnippets.net/js:17:3

Observe o foo.name is: fooe Error...at foo.

No Chrome 50 e versões anteriores, Firefox 52 e versões anteriores e Edge sem o sinalizador experimental, você verá isso porque elas ainda não têm a Function#namepropriedade:

foo.name é: 
Erro
    no foo (http://stacksnippets.net/js:14:23)
    em http://stacksnippets.net/js:17:3

Observe que o nome está ausente foo.name is:, mas é mostrado no rastreamento da pilha. É que, na verdade, implementar a name propriedade na função era de menor prioridade do que alguns outros recursos do ES2015; Chrome e Firefox têm agora; O Edge está atrás de uma bandeira, presumivelmente não ficará muito tempo atrás da bandeira.

Obviamente, só posso usar esta função depois de defini-la

Corrigir. Não há sintaxe de declaração de função para funções de seta, apenas sintaxe de expressão de função e não há seta equivalente ao nome em uma expressão de função denominada no estilo antigo ( var f = function foo() { };). Portanto, não há equivalente a:

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

Você precisa dividi-lo em duas expressões (eu diria que você deveria fazer isso de qualquer maneira ) :

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

Obviamente, se você precisar colocar isso onde uma única expressão é necessária, sempre poderá usar ... uma função de seta:

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

Não estou dizendo que isso seja bonito, mas funciona se você absolutamente, positivamente, precisar de um invólucro de expressão única.

TJ Crowder
fonte
Como evito que o nome seja definido (como nos mecanismos mais antigos)?
trusktr
1
@trusktr: Não consigo entender por que você gostaria de fazê-lo, mas se quiser evitá-lo: não faça as coisas acima. Por exemplo, embora let f = () => { /* do something */ };atribua um nome à função, let g = (() => () => { /* do something */ })();não.
TJ Crowder
Foi o que acabei fazendo. Prefiro que as classes anônimas geradas pela minha biblioteca de herança de classe sejam anônimas do que nomes de variáveis ​​internas nas quais o usuário da minha biblioteca de herança de classes não precisa pensar e gostaria que o usuário final visse um nome somente se eles fornecerem um nome para a classe. ( github.com/trusktr/lowclass )
trusktr 31/03
4
@trusktr: A única exceção que eles fizeram pode atender às suas finalidades, então: Se você atribuir uma função a uma propriedade em um objeto existente , o nome não será definido: o.foo = () => {};não dará um nome à função. Isso foi intencional para evitar vazamento de informações.
TJ Crowder
86

Não. A sintaxe da seta é um atalho para funções anônimas. Funções anônimas são, bem, anônimas.

As funções nomeadas são definidas com a functionpalavra - chave.

Jörg W Mittag
fonte
16
Como afirmado por @ DenysSéguret, não é um atalho para funções anônimas . Você obterá uma função vinculada . Você pode ver o resultado transpiled aqui bit.do/es2015-arrow para entender melhor o que isso implica ...
sminutoli
16
A primeira palavra desta resposta está correta para a pergunta feita, porque o OP descartou a maneira como você nomeia uma função de seta. Mas o restante da resposta está simplesmente incorreto. Procure na especificação onde a operação abstrataSetFunctionName é usada. let a = () => {};define uma função nomeada (o nome é a) de acordo com as especificações e nos mecanismos modernos (gera um erro para ver); eles apenas ainda não suportam a namepropriedade (mas está nas especificações e chegarão lá eventualmente).
TJ Crowder
4
@ DenysSéguret: Você pode simplesmente nomeá-los, está nas especificações. Você faz da maneira que o OP descartou na pergunta, que dá à função (não apenas a variável) um nome verdadeiro.
TJ Crowder
5
@TJCrowder Obrigado por esse pouco de informação e por sua resposta útil . Eu não conhecia esse recurso (muito estranho).
Denys Séguret 12/08/19
4
Esta resposta não está correta. A pergunta foi respondida corretamente por @TJCrowder abaixo
Drenai
44

Se por 'nomeado', você quer dizer que deseja que a .namepropriedade da sua função de seta seja definida, você está com sorte.

Se uma função de seta estiver definida no lado direito de uma expressão de atribuição, o mecanismo pegará o nome no lado esquerdo e o utilizará para definir as funções da seta .name, por exemplo

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

sayHello.name //=== 'sayHello'

Dito isto, sua pergunta parece ser mais 'posso obter uma função de seta para içar?'. A resposta para isso é um grande e velho "não", receio.

hughfdjackson
fonte
1
Na versão atual do chrome, pelo menos, sayHello.name ainda está ""; Observe que, como todas as funções, sayHello é um objeto, para que você possa definir propriedades arbitrárias, se desejar. Você não pode escrever para "nome", pois é somente leitura, mas pode dizerHello.my_name = "sayHello"; Mas não tenho certeza do que isso leva a você, já que você não pode fazer / referenciar isso dentro do corpo da função.
Dtipson
2
Eu estava errado: enquanto o nome não é diretamente mutável, você pode configurá-lo assim: Object.defineProperty (sayHello, 'name', {value: 'sayHello'})
Dtipson
1
"Se uma função de seta é definida no lado direito [..]" qual é a fonte disso? Não consigo encontrá-lo nas especificações. Só preciso de uma referência para citar.
Gajus
@Dtipson: Isso ocorre apenas porque os motores modernos ainda não implementaram a configuração da namepropriedade em todos os locais exigidos pelas especificações do ES2015; eles vão conseguir. É uma das últimas coisas na lista de conformidade do Chrome e nem o Chrome 50 o possui (o Chrome 51 finalmente o fará). Detalhes . Mas o OP descartou especificamente fazer isso (estranhamente).
TJ Crowder
Posso obter esse nome no toString? Tentei substituí-lo, mas não sei como acessar o corpo da função.
Qwerty
0

Parece que isso será possível com o ES7: https://babeljs.io/blog/2015/06/07/react-on-es6-plus#arrow-functions

O exemplo dado é:

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

O corpo das funções de seta do ES6 compartilham o mesmo léxico que o código que as envolve, o que obtém o resultado desejado devido à maneira como os inicializadores de propriedades do ES7 têm escopo definido.

Observe que, para que isso funcione com babel, eu precisava habilitar a sintaxe mais experimental do estágio 0 do ES7. No meu arquivo webpack.config.js, atualizei o babel loader da seguinte maneira:

{test: /\.js$/, exclude: /node_modules/, loader: 'babel?stage=0'},
Hamish Currie
fonte
6
Essa não é exatamente uma função de seta nomeada. Este é um atalho para criar métodos de instância no construtor, e eu me pergunto como é relevante aqui?
Bergi
Oi @Bergi, não tenho certeza se essa foi a intenção do autor original, mas estava tentando criar uma função nomeada com o mesmo escopo que o objeto. Se não usarmos essa técnica, e quisermos ter o escopo apropriado, precisamos incluí-la no construtor da classe. this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
Hamish Currie
8
Você pode usar com a mesma facilidade constructor() { this.handleOptionsButtonClick = (e) => {…}; }que é totalmente equivalente ao código na sua resposta, mas que já funciona no ES6. Não há necessidade de usar .bind.
Bergi
1
Btw, eu acho que você está confundindo "método (exemplo)" e "escopo" com " "função chamada" com thiscontexto"
Bergi
1
@tdecs: Sim, você pode; Jörg está enganado. let a = () => {};define uma função de seta nomeada chamada a. Detalhes .
TJ Crowder
0

Na verdade, parece que uma maneira de nomear funções de seta (pelo menos a partir do chrome 77 ...) é fazer isso:

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

insira a descrição da imagem aqui

Devin G Rhode
fonte
poderia, teoricamente, criar uma macro babel fn('yourName', ()=>{}), o que poderia manter 100% de função de seta correta semântica, ou melhor ainda um plugin babel para "chamado sintaxe da função seta": fn yourName() => {}. Qualquer um desses compilaria até o código acima. Acho setas nomeados seria tão BADA55
Devin G Rhode
-1

para escrever a função de seta nomeada, você pode usar o exemplo abaixo, onde eu tenho uma classe chamada LoginClass e, dentro dessa classe, escrevi uma seta chamada função , denominada successAuth classe LoginClass {

    constructor() {

    }

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

    }

}
BERGUIGA Mohamed Amine
fonte
1
É sempre melhor adicionar uma explicação ao código que você está postando como resposta, para ajudar os visitantes a entender por que essa é uma boa resposta.
22416 abarisone
Isso faz o que o OP disse que não quer fazer, e confia nessa proposta, que está apenas na Fase 2 e provavelmente nem fará o ES2017 (mas acho que é provável que faça o ES2018, e os transpilers a apoiaram) por meses). Ele também efetivamente duplica várias respostas anteriores, o que não é útil.
TJ Crowder
-2

ESTE É ES6

Sim, acho que o que você procura é algo assim:

const foo = (depth) => {console.log("hi i'm Adele")}
foo -> // the function itself
foo() -> // "hi i'm Adele"
Venkatesh Namburi
fonte
Eu tentei este exemplo, funciona bem. Alguma boa razão para dar votos negativos? Esse uso cria efeitos colaterais indesejados ou estou perdendo algum ponto importante? Sua ajuda para esclarecer minha dúvida será apreciada.
FullMoon
@GaneshAdapa: Espero que os votos negativos sejam porque isso sugere sugerir exatamente o que o OP disse que não queria fazer, sem fornecer mais informações.
TJ Crowder
-2

Você pode pular a parte da função e a parte da seta para criar funções. Exemplo:

 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
fonte