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 thislexicamente e oferecendo sintaxe concisa.
Contudo,
- Não se pode sempre ligar
thislexicamente
- 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 functionexclusivamente.
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 thisdentro de um retorno de chamada. Já existem várias maneiras de fazer isso: Pode-se atribuir thisa uma variável, usar bindou usar o terceiro argumento disponível nos Arraymé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 thisespecialmente. Agora, existem dois thisvalores 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 functionpara eachpoder ligar thisdinamicamente. Não podemos usar uma função de seta aqui.
Lidar com vários thisvalores também pode ser confuso, porque é difícil saber sobre o que thisum 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 thise 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 thisvalores 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 thispossa sempre ser ligado dinamicamente) e sempre faça referência thisatravés de uma variável. Variáveis são lexicais e assumem muitos nomes. A atribuição thisa 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 thisa uma variável (mesmo quando houver uma única thisou 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 thisnã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 thisdinamicamente:
- 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
EventTargetcom 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ê thisjá 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 ifinstruçõ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 thisconforme 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 returnserá 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 functionexclusivamente 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
thisa uma variável. Não use () => {}.
Fixed this bound to scope at initialisationuma limitação?