De acordo com a proposta , as flechas tinham como objetivo "abordar e resolver vários pontos problemáticos comuns dos tradicionais Function Expression
". Eles pretendiam melhorar as coisas ligando-se this
lexicamente e oferecendo sintaxe concisa.
Contudo,
- Não se pode sempre ligar
this
lexicamente
- A sintaxe da função de seta é delicada e ambígua
Portanto, as funções de seta criam oportunidades de confusão e erros e devem ser excluídas do vocabulário de um programador JavaScript, substituídas por function
exclusivamente.
Em relação ao léxico this
this
é problemático:
function Book(settings) {
this.settings = settings;
this.pages = this.createPages();
}
Book.prototype.render = function () {
this.pages.forEach(function (page) {
page.draw(this.settings);
}, this);
};
As funções de seta pretendem corrigir o problema em que precisamos acessar uma propriedade this
dentro de um retorno de chamada. Já existem várias maneiras de fazer isso: Pode-se atribuir this
a uma variável, usar bind
ou usar o terceiro argumento disponível nos Array
métodos agregados. No entanto, as setas parecem ser a solução mais simples, portanto o método pode ser refatorado da seguinte maneira:
this.pages.forEach(page => page.draw(this.settings));
No entanto, considere se o código usou uma biblioteca como jQuery, cujos métodos se ligam this
especialmente. Agora, existem dois this
valores para lidar:
Book.prototype.render = function () {
var book = this;
this.$pages.each(function (index) {
var $page = $(this);
book.draw(book.currentPage + index, $page);
});
};
Devemos usar function
para each
poder ligar this
dinamicamente. Não podemos usar uma função de seta aqui.
Lidar com vários this
valores também pode ser confuso, porque é difícil saber sobre o que this
um autor estava falando:
function Reader() {
this.book.on('change', function () {
this.reformat();
});
}
O autor realmente pretendeu telefonar Book.prototype.reformat
? Ou ele esqueceu de ligar this
e pretendeu ligar Reader.prototype.reformat
? Se mudarmos o manipulador para uma função de seta, nos perguntaremos da mesma forma se o autor queria a dinâmica this
, mas escolheu uma seta porque ela se encaixava em uma linha:
function Reader() {
this.book.on('change', () => this.reformat());
}
Pode-se colocar: "É excepcional que as setas às vezes possam ser a função errada a ser usada? Talvez se raramente precisemos de this
valores dinâmicos , ainda seria bom usar setas na maioria das vezes."
Mas pergunte a si mesmo: "Valerá a pena depurar código e descobrir que o resultado de um erro foi causado por um 'caso de ponta?'" "Prefiro evitar problemas não apenas na maioria das vezes, mas 100% do tempo.
Existe uma maneira melhor: sempre use function
(para que this
possa sempre ser ligado dinamicamente) e sempre faça referência this
através de uma variável. Variáveis são lexicais e assumem muitos nomes. A atribuição this
a uma variável tornará suas intenções claras:
function Reader() {
var reader = this;
reader.book.on('change', function () {
var book = this;
book.reformat();
reader.reformat();
});
}
Além disso, sempre atribuir this
a uma variável (mesmo quando houver uma única this
ou nenhuma outra função) garante que as intenções permaneçam claras, mesmo após a alteração do código.
Além disso, a dinâmica this
não é excepcional. O jQuery é usado em mais de 50 milhões de sites (até o momento em que este artigo foi escrito em fevereiro de 2016). Aqui estão outras APIs vinculadas this
dinamicamente:
- O Mocha (~ 120k de downloads ontem) expõe métodos para seus testes via
this
.
- O Grunt (~ 63k downloads ontem) expõe métodos para criar tarefas via
this
.
- O backbone (~ 22k downloads ontem) define os métodos de acesso
this
.
- APIs de eventos (como os DOMs) se referem a um
EventTarget
com this
.
- APIs prototípicas corrigidas ou estendidas referem-se a instâncias com
this
.
(Estatísticas via http://trends.builtwith.com/javascript/jQuery e https://www.npmjs.com .)
É provável que você this
já exija ligações dinâmicas .
Um léxico this
é às vezes esperado, mas às vezes não; assim como uma dinâmica this
às vezes é esperada, mas às vezes não. Felizmente, existe uma maneira melhor, que sempre produz e comunica a ligação esperada.
Em relação à sintaxe concisa
As funções de seta conseguiram fornecer uma "forma sintática mais curta" para as funções. Mas essas funções mais curtas o tornarão mais bem-sucedido?
É x => x * x
"mais fácil de ler" do que function (x) { return x * x; }
? Talvez seja, porque é mais provável que produza uma única linha curta de código. De acordo com Dyson A influência da velocidade de leitura e do comprimento da linha na eficácia da leitura na tela ,
Um comprimento de linha médio (55 caracteres por linha) parece suportar uma leitura eficaz em velocidades normais e rápidas. Isso produziu o mais alto nível de compreensão. . .
Justificativas semelhantes são feitas para o operador condicional (ternário) e para if
instruções de linha única .
No entanto, você está realmente escrevendo as funções matemáticas simples anunciadas na proposta ? Meus domínios não são matemáticos, portanto minhas sub-rotinas raramente são tão elegantes. Em vez disso, normalmente vejo as funções de seta quebrando um limite de coluna e quebrando em outra linha devido ao editor ou guia de estilo, que anula "legibilidade" pela definição de Dyson.
Alguém pode colocar: "Que tal usar apenas a versão curta para funções curtas, quando possível?" Mas agora uma regra estilística contradiz uma restrição de linguagem: "Tente usar a menor notação de função possível, tendo em mente que às vezes apenas a notação mais longa será vinculada this
conforme o esperado". Essa confusão torna as flechas particularmente propensas ao mau uso.
Existem vários problemas com a sintaxe da função de seta:
const a = x =>
doSomething(x);
const b = x =>
doSomething(x);
doSomethingElse(x);
Ambas as funções são sintaticamente válidas. Mas doSomethingElse(x);
não está no corpo de b
, é apenas uma afirmação de nível superior mal-recuada.
Ao expandir para o formulário de bloco, não há mais um implícito return
, que poderia ser esquecido de restaurar. Mas a expressão pode ter apenas o objetivo de produzir um efeito colateral, então quem sabe se um explícito return
será necessário daqui para frente?
const create = () => User.create();
const create = () => {
let user;
User.create().then(result => {
user = result;
return sendEmail();
}).then(() => user);
};
const create = () => {
let user;
return User.create().then(result => {
user = result;
return sendEmail();
}).then(() => user);
};
O que pode ser pretendido como um parâmetro de descanso pode ser analisado como o operador de spread:
processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest
A atribuição pode ser confundida com argumentos padrão:
const a = 1;
let x;
const b = x => {}; // No default
const b = x = a => {}; // "Adding a default" instead creates a double assignment
const b = (x = a) => {}; // Remember to add parens
Os blocos parecem objetos:
(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object
O que isto significa?
() => {}
O autor pretendia criar um no-op ou uma função que retornasse um objeto vazio? (Com isso em mente, devemos colocar o local {
depois =>
? Devemos nos restringir apenas à sintaxe da expressão? Isso reduziria ainda mais a frequência das setas.)
=>
parece <=
e >=
:
x => 1 ? 2 : 3
x <= 1 ? 2 : 3
if (x => 1) {}
if (x >= 1) {}
Para invocar uma expressão de função de seta imediatamente, é preciso colocar ()
do lado de fora, mas a colocação ()
do lado de dentro é válida e pode ser intencional.
(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function
Embora, se alguém escrever (() => doSomething()());
com a intenção de escrever uma expressão de função chamada imediatamente, simplesmente nada acontecerá.
É difícil argumentar que as funções de seta são "mais compreensíveis", com todos os casos acima em mente. Um pode aprender todas as regras especiais necessárias para utilizar esta sintaxe. Isso realmente vale a pena?
A sintaxe de function
é excepcionalmente generalizada. Usar function
exclusivamente significa que a própria linguagem impede que alguém escreva códigos confusos. Para escrever procedimentos que devem ser sintaticamente entendidos em todos os casos, eu escolho function
.
Em relação a uma diretriz
Você solicita uma orientação que precisa ser "clara" e "consistente". O uso de funções de seta acabará resultando em código sintaticamente válido e logicamente inválido, com ambas as formas de função entrelaçadas, significativa e arbitrariamente. Portanto, ofereço o seguinte:
Diretriz para Notação de Função no ES6:
- Sempre crie procedimentos com
function
.
- Sempre atribua
this
a uma variável. Não use () => {}
.
Fixed this bound to scope at initialisation
uma limitação?