Funções que retornam uma função

109

Estou preso a este conceito de 'Funções que retornam funções'. Estou me referindo ao livro 'Javascript Orientado a Objetos' de Stoyan Stefanov.

Snippet One:

    function a() {
      
        alert('A!');
    
        function b(){
            alert('B!'); 
        }
    
        return b();
    }
    
    var s = a();
    alert('break');
    s();

Resultado:

A!
B!
break

Snippet Two

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b;
}

var s = a();
alert('break');
s();
Resultado:

A!
break
B!

Alguém pode me dizer a diferença entre retornar be b()nos trechos acima?

Cafecorridor
fonte
2
Você notará que o primeiro fragmento apresenta um erro em s ();
weirdalsuperfan

Respostas:

121

Atribuir uma variável a uma função (sem o parêntese) copia a referência à função. Colocando o parêntese no final do nome de uma função, chama a função, retornando o valor de retorno da função.

Demo

function a() {
    alert('A');
}
//alerts 'A', returns undefined

function b() {
    alert('B');
    return a;
}
//alerts 'B', returns function a

function c() {
    alert('C');
    return a();
}
//alerts 'C', alerts 'A', returns undefined

alert("Function 'a' returns " + a());
alert("Function 'b' returns " + b());
alert("Function 'c' returns " + c());

Em seu exemplo, você também está definindo funções dentro de uma função. Tal como:

function d() {
    function e() {
        alert('E');
    }
    return e;
}
d()();
//alerts 'E'

A função ainda pode ser chamada. Ainda existe. Isso é usado em JavaScript o tempo todo. As funções podem ser passadas ao redor apenas como outros valores. Considere o seguinte:

function counter() {
    var count = 0;
    return function() {
        alert(count++);
    }
}
var count = counter();
count();
count();
count();

A função count pode manter as variáveis ​​que foram definidas fora dela. Isso é chamado de encerramento. Também é muito usado em JavaScript.

kzh
fonte
Snippet um: var hero = {name: 'Rafaelo', sayName: function () {return hero.name; }, nayName: hero.sayName} hero.nayName (); Snippet dois: var hero = {name: 'Rafaelo', sayName: function () {return hero.name; }, nayName: this.sayName} hero.nayName (); O primeiro snippet me dá a saída correta, enquanto o segundo não. Por quê? Saudações.
Cafecorridor,
thissignifica apenas algo dentro de um corpo de função, caso contrário, é global. O que você está dizendo this.sayNameé que deseja a variável global sayNameque não existe, é indefinida, portanto, não pode ser chamada.
kzh
7
Fiquei confuso com o d () (); no início, mas depois percebi que o primeiro () chama de o segundo () chama o valor de retorno de d, que é e.
skud
1
Oito anos depois e isso ainda é relevante!
Brad Vanderbush
45

Retornar o nome da função sem ()retorna uma referência à função, que pode ser atribuída como você fez var s = a(). sagora contém uma referência à função b()e chamar s()é funcionalmente equivalente a chamar b().

// Return a reference to the function b().
// In your example, the reference is assigned to var s
return b;

Chamar a função com ()em uma instrução de retorno executa a função e retorna qualquer valor retornado pela função. É semelhante a chamar var x = b();, mas em vez de atribuir o valor de retorno de b()você o está retornando da função de chamada a(). Se a b()própria função não retornar um valor, a chamada retornará undefinedapós qualquer outro trabalho realizado por b().

// Execute function b() and return its value
return b();
// If b() has no return value, this is equivalent to calling b(), followed by
// return undefined;
Michael Berkowski
fonte
1
De todas as respostas, gostei mais da sua resposta devido à sua simplicidade.
anar khalilov
Bem colocado! Muito fácil de entender esta resposta.
AndrewSteinheiser de
Obrigado por validar a questão sobre se a função não retorna um valor, a chamada retorna indefinido. Descobri essa confusão recentemente: mesmo se a função retornar nulo explicitamente, se você atribuir o valor retornado a uma variável, ele será indefinido, não nulo. Imagino que isso tenha causado muitos problemas estranhos em algumas bases de código, porque null e undefined não são absolutamente equivalentes a ===.
Benjamin
37

return b(); chama a função b () e retorna seu resultado.

return b; retorna uma referência à função b, que você pode armazenar em uma variável para chamar mais tarde.

Abdo
fonte
17

Retornar bé retornar um objeto de função. Em Javascript, as funções são apenas objetos, como qualquer outro objeto. Se você achar que isso não ajuda, apenas substitua a palavra "objeto" por "coisa". Você pode retornar qualquer objeto de uma função. Você pode retornar um valor verdadeiro / falso. Um número inteiro (1,2,3,4 ...). Você pode retornar uma string. Você pode retornar um objeto complexo com várias propriedades. E você pode retornar uma função. uma função é apenas uma coisa.

No seu caso, retornar bretorna a coisa, a coisa é uma função que pode ser chamada. Retornar b()retorna o valor retornado pela função que pode ser chamada.

Considere este código:

function b() {
   return 42;
}

Usando a definição acima, return b();retorna o valor 42. Por outro lado, return b;retorna uma função, que por sua vez retorna o valor 42. São duas coisas diferentes.

Cheeso
fonte
4
deve retornar 42;)
c69,
4

Quando você retorna b, é apenas uma referência à função b, mas não está sendo executada neste momento.

Ao retornar b(), você está executando a função e retornando seu valor.

Tente alerting typeof(s)em seus exemplos. O trecho b lhe dará 'função'. O que um snippet dará a você?

Vzwick
fonte
O primeiro dá 'indefinido'. Isso significa que return b () é completamente inútil? Além disso, no segundo fragmento, a função b é privada. Como então podemos acessar a referência fora da função? Forneça-me um link que explica esse conceito claramente, se possível. Obrigado!
Cafecorridor,
Eu tenho a resposta para a primeira. retorna 1 + 2 na função b () e o typeof mostra o número. Obrigado.
Cafecorridor,
Fico feliz que você tenha entendido! Quanto à função privada: não é realmente privada no segundo exemplo, pois você já a retornou. Na verdade, ele é atribuído a s. Tente em return thisvez de return bembora ... Você será capaz de fazer s.b()então;)
vzwick
Vou experimentar com certeza. Ainda não atingiu esse conceito em Javascript. Talvez em alguns dias. Obrigado! :)
Cafecorridor
função a () {alerta ("A!"); função b () {alerta ("B!"); } return b; } var s = a (); delete a; s (); ---- fim ---- O conceito de referência em Javascript é o mesmo que em Java? Aqui, eu excluí a função a () e ainda uma chamada para s () executa b (). Então, posso dizer que s contém uma cópia de be não está apontando para b () definido em a ()?
Cafecorridor,
2

Imagine a função como um tipo, como um int. Você pode retornar ints em uma função. Você também pode retornar funções, elas são objetos do tipo "função".

Agora o problema de sintaxe: porque funções retornam valores, como você pode retornar uma função e não seu valor de retorno?

omitindo colchetes! Porque sem colchetes, a função não será executada! Assim:

return b;

Irá retornar a "função" (imagine como se você estivesse retornando um número), enquanto:

return b();

Primeiro executa a função e depois retorna o valor obtido ao executá-la, é uma grande diferença!

Francesco Belladonna
fonte
No segundo snippet, a função b é privada. Como então podemos acessar a referência fora da função? Existem regras que regem o mesmo?
Cafecorridor,
Objetos em JavaScript (isso inclui funções) não são mais privados se você compartilhá-los.
kzh,
@Cafecorridor: Se a função privada é retornada por algo (uma função pública) ou é atribuída a uma variável pública (bem, uma variável acessível pelo aplicativo), você pode facilmente fazer sua variável (); , caso contrário, atribua a função retornada a uma variável e execute novamente sua variável ();
Francesco Belladonna,
2

Crie uma variável :

var thing1 = undefined;

Declare uma função :

function something1 () {
    return "Hi there, I'm number 1!";
}

Alerte o valor de thing1(nossa primeira variável):

alert(thing1); // Outputs: "undefined".

Agora, se quiséssemos thing1ser uma referência para a função something1, o que significa que seria a mesma coisa que nossa função criada, faríamos:

thing1 = something1;

No entanto, se quisermos o return valor da função, devemos atribuir a ela o valor de retorno da função executada. Você executa a função usando parênteses:

thing1 = something1(); // Value of thing1: "Hi there, I'm number 1!" 
Shaz
fonte
-1

Snippet um:

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b(); //return nothing here as b not defined a return value
}

var s = a(); //s got nothing assigned as b() and thus a() return nothing.
alert('break');
s(); // s equals nothing so nothing will be executed, JavaScript interpreter will complain

a declaração 'b ()' significa executar a função chamada 'b' que mostra uma caixa de diálogo com o texto 'B!'

a instrução 'return b ();' significa executar uma função chamada 'b' e então retornar qual função 'b' retornou. mas 'b' não retorna nada, então esta instrução 'return b ()' também não retorna nada. Se b () retornar um número, então 'return b ()' também será um número.

Agora 's' é atribuído o valor do que 'a ()' retorna, que retorna 'b ()', que não é nada, então 's' não é nada (em JavaScript é uma coisa na verdade, é um 'indefinido'. quando você pede ao JavaScript para interpretar que tipo de dados é o 's', o interpretador JavaScript lhe dirá 's' é um indefinido.) Como 's' é um indefinido, quando você pede ao JavaScript para executar esta instrução 's ()', você está pedindo ao JavaScript para executar uma função chamada 's', mas 's' aqui é um 'indefinido', não uma função, então o JavaScript reclamará, "ei, s não é uma função, não sei como a fazer com este s ", então uma mensagem de erro" Uncaught TypeError: s não é uma função "será mostrada por JavaScript (testado no Firefox e Chrome)


Snippet Two

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b; //return pointer to function b here
}

var s = a();  //s get the value of pointer to b
alert('break');
s(); // b() function is executed

agora, a função 'a' retornando um ponteiro / alias para uma função chamada 'b'. então, quando executar 's = a ()', 's' obterá um valor apontando para b, ou seja, 's' é um apelido de 'b' agora, chamar 's' é igual a chamar 'b'. ie 's' é uma função agora. Execute 's ()' significa executar a função 'b' (o mesmo que executar 'b ()'), uma caixa de diálogo mostrando 'B!' aparecerá (ou seja, executando a instrução 'alert (' B! '); na função' b ')

Piloto Henry no trabalho
fonte
-2

Isso é muito útil na vida real.

Trabalhando com Express.js

Portanto, seu expresstrajeto normal é assim:

function itWorksHandler( req, res, next ) {
  res.send("It works!");
}

router.get("/check/works", itWorksHandler );

Mas e se você precisar adicionar algum wrapper, manipulador de erros ou smth?

Em seguida, você invoca sua função de um invólucro.

function loggingWrapper( req, res, next, yourFunction ) {
  try {
    yourFunction( req, res );
  } catch ( err ) {
    console.error( err );
    next( err );
  }
}

router.get("/check/works", function( req, res, next ) {
  loggingWrapper( req, res, next, itWorksHandler );
});

Parece complicado? Bem, que tal isto:

function function loggingWrapper( yourFunction ) => ( req, res, next ) {
  try {
    yourFunction( req, res, next );
  } catch ( err ) {
    console.error( err );
    next( err );
  }
}

router.get("/check/works", loggingWrapper( itWorksHandler ) );

Veja no final que você está passando uma função loggingWrappercom um argumento como outra função itWorksHandler, e você loggingWrapperretorna uma nova função que recebe req, res, nextcomo argumentos.

Zilvinas
fonte