Você pode vincular 'this' em uma função de seta?

133

Estou experimentando o ES6 há um tempo e acabei de encontrar um pequeno problema.

Eu realmente gosto de usar as funções de seta e sempre que posso, eu as uso.

No entanto, parece que você não pode vinculá-los!

Aqui está a função:

var f = () => console.log(this);

Aqui está o objeto ao qual quero vincular a função:

var o = {'a': 42};

E aqui está como eu me ligaria fa o:

var fBound = f.bind(o);

E então eu posso simplesmente ligar fBound:

fBound();

O que produzirá este (o oobjeto):

{'a': 42}

Legal! Encantador! Exceto que isso não funciona. Em vez de emitir o oobjeto, ele gera o windowobjeto.

Então, eu gostaria de saber: você pode vincular funções de seta? (E se sim, como?)


Testei o código acima no Google Chrome 48 e Firefox 43, e o resultado é o mesmo.

Florrie
fonte
29
O ponto principal das funções das setas é que elas usam o thisescopo pai.
Loganfsmyth 23/10

Respostas:

158

Você não pode religar this em uma função de seta. Será sempre definido como o contexto em que foi definido. Se você precisar thisser significativo, use uma função normal.

Do ECMAScript 2015 Spec :

Qualquer referência a argumentos, super, this ou new.target em um ArrowFunction deve resolver uma ligação em um ambiente que inclua lexicamente. Normalmente, este será o ambiente de funções de uma função que encerra imediatamente.

Nick Tomlin
fonte
7
A primeira frase desta resposta está errada. Provavelmente porque a questão também é confusa e vinculativa this. Os dois estão relacionados, mas não são os mesmos. thisdentro de uma função de seta sempre 'herda' o thisdo escopo. Essa é uma característica das funções de seta. Mas você ainda pode bindtodos os outros parâmetros de uma função de seta. Apenas não this.
Stijn de Witt
54

Para concluir, você pode reconectar as funções das setas, apenas não pode alterar o significado de this.

bind ainda tem valor para argumentos da função:

((a, b, c) => {
  console.info(a, b, c) // 1, 2, 3
}).bind(undefined, 1, 2, 3)()

Experimente aqui: http://jsbin.com/motihanopi/edit?js,console

cvazac
fonte
1
Existe uma maneira de detectar ou usar qualquer objeto ao qual a função (seta) esteja vinculada, sem fazer referência this(que é, obviamente, definida lexicamente)?
Florrie
3
Use um argumento para o contexto: ((context) => {someOtherFunction.apply (context)}). Bind (willBeIgnored, context) () #
276 cvazac
13

Do MDN :

Uma expressão de função de seta possui uma sintaxe mais curta em comparação com as expressões de função e vincula lexicamente o valor this (não vincula seus argumentos this, argumentos, super ou new.target). As funções de seta são sempre anônimas.

Isso significa que você não pode vincular um valor thiscomo desejar.

Sterling Archer
fonte
6

Por anos, os desenvolvedores do js lutaram com a vinculação de contexto, perguntaram por que thismudaram no javascript, tanta confusão ao longo dos anos devido à vinculação de contexto e a diferença entre o significado de thisjavascript e thisna maioria das outras linguagens OOP.

Tudo isso me leva a perguntar, por que, por que! por que você não queria reativar uma função de seta! Aqueles onde criado especialmente para resolver todos estes problemas e confusões e evitar ter que usar bindou callou qualquer outra forma de preservar o escopo da função.

TL; DR

Não, você não pode religar as funções de seta.

taxicala
fonte
7
As funções de seta foram criadas antes de tudo para fornecer uma sintaxe mais limpa e rápida. Pense no cálculo lambda. Pena que os fanáticos por OO que importunam a vida de programadores JS funcionais aproveitaram a oportunidade para tirar a liberdade de especificar o contexto de chamada.
Marco Faustinelli
5
Usar thisnão é funcional. lambdas deve ser arguments => output. Se você precisar de algum contexto externo, transmita-o. A própria existência disso thisé o que facilitou toda a manutenção de padrões OO na linguagem. Você nunca teria ouvido o termo "classe javascript" sem ele.
erich2k8
3
Você pode religar as funções de seta. Apenas não this.
Stijn de Witt
6

Você não pode bindalterar o valor de thisdentro de uma função de seta. No entanto, você pode criar uma nova função regular que faça a mesma coisa que a seta antiga e, em seguida, use callou bindpara reconfigurar thisnormalmente.

Usamos uma evalchamada aqui para recriar a função de seta que você passa como uma função normal e, em seguida, usa-a callpara invocá-la com uma função diferente this:

var obj = {value: 10};
function arrowBind(context, fn) {
  let arrowFn;
  (function() {
    arrowFn = eval(fn.toString());
    arrowFn();
  }).call(context);
}
arrowBind(obj, () => {console.log(this)});

o nome
fonte
3
Obrigado por compartilhar este exemplo de uma maneira de vincular uma função de seta a um contexto diferente! Para facilitar a resposta dos futuros leitores, talvez você possa editá- la para descrever como e por que o código funciona?
Florrie
4

As funções de seta do ES6 realmente resolvem "isso" em JavaScript

O link acima explica que as funções das setas thisnão mudam com as bind, call, applyfunções.

É explicado com um exemplo muito bom.

execute isso node v4para ver o comportamento "esperado",

this.test = "attached to the module";
var foo = { test: "attached to an object" };
foo.method = function(name, cb){ 
    // bind the value of "this" on the method 
    // to try and force it to be what you want 
    this[name] = cb.bind(this); };
foo.method("bar", () => { console.log(this.test); });
foo.bar(); 
Prithvi Raj Vuppalapati
fonte
Solicitar que você forneça informações mais relevantes na própria resposta, pode ser um código de exemplo ou versões editadas de perguntas
jithin Scaria
// execute isso no nó v4 para ver o comportamento "esperado" this.test = "anexado ao módulo"; var foo = {test: "anexado a um objeto"}; foo.method = function (name, cb) {// vincula o valor "this" no método // para tentar forçá-lo a ser o que você deseja this [name] = cb.bind (this); }; foo.method ("bar", () => {console.log (this.test);}); foo.bar ();
Prithvi Raj Vuppalapati 02/07
O interessante aqui é tentar fazer isso e tentar novamente substituindo foo.method ('bar', () => {console.log (this.test);}); com foo.method ('bar', function () {console.log (this.test);}); - os logs da primeira versão "anexados ao módulo" e os segundos logs "anexados ao objeto" - eu realmente prefiro o tratamento mais estável de "this" usando as funções de seta. Você ainda pode obter os outros efeitos usando padrões diferentes e os resultados são IMO mais legíveis e previsíveis.
Methodician
3

Fiz a mesma pergunta há alguns dias.

Você não pode vincular um valor, pois o thisjá está vinculado.

Vinculando este escopo diferente ao operador ES6 => function

fos.alex
fonte
4
"uma vez que isto já thisestá vinculado " - Nas funções regulares também está vinculado. O ponto é que, nas funções de seta, ele não possui ligação local .
um oliver melhor
2

Resumindo, você NÃO pode vincular funções de seta, mas continue lendo:

Imagine que você tem esta função de seta abaixo da qual é impressa thisno console:

const myFunc = ()=> console.log(this);

Portanto, a solução rápida para isso seria usar a função normal, então altere-a para:

function myFunc() {console.log(this)};

Em seguida, você pode vinculá-lo a qualquer ambiente lexical usando bindou callou apply:

const bindedFunc = myFunc.bind(this);

e chamá-lo em caso de bind.

bindedFunc();

Também existem maneiras de usá eval()-lo, o que não é recomendado .

Alireza
fonte
function myFuncé comportamentalmente diferente, além de ser vinculável; uma correspondência mais próxima seria const myFunc = function() {...}. Também estou curioso sobre o que você quer dizer com eval, já que essa é uma abordagem que acho que nenhuma resposta já foi compartilhada aqui antes - seria interessante ver como isso é feito e depois ler por que é tão fortemente desencorajado.
Florrie 21/01
1

Talvez este exemplo ajude você:

let bob = {
   _name: "Bob",
   _friends: ["stackoverflow"],
   printFriends:(x)=> {
      x._friends.forEach((f)=> {
         console.log(x._name + " knows " + f);
      });
   }
}

bob.printFriends = (bob.printFriends).bind(null,bob);
bob.printFriends();

Ehsan
fonte
coisas boas ... levemente interessante
Aleks