Passar uma função JavaScript como parâmetro


665

Como passo uma função como parâmetro sem que a função seja executada na função "pai" ou usada eval()? (Desde que eu li que é inseguro.)

Eu tenho isto:

addContact(entityId, refreshContactList());

Funciona, mas o problema é que é refreshContactListacionado quando a função é chamada, e não quando é usada na função.

Eu poderia contornar isso usando eval(), mas não é a melhor prática, de acordo com o que li. Como posso passar uma função como parâmetro em JavaScript?

Respostas:


930

Você só precisa remover o parêntese:

addContact(entityId, refreshContactList);

Isso passa a função sem executá-la primeiro.

Aqui está um exemplo:

function addContact(id, refreshCallback) {
    refreshCallback();
    // You can also pass arguments if you need to
    // refreshCallback(id);
}

function refreshContactList() {
    alert('Hello World');
}

addContact(1, refreshContactList);

6
@ stevefenton, considere atualizar sua resposta com o comentário h2ooooooo, seria muito útil.
Morgan Wilde

5
Com base na pergunta, o retorno de chamada não aceita parâmetros, e é por isso que os deixei de fora do exemplo. Vou adicionar um comentário sobre isso.
Fenton

4
@Veverke Ficaria assim ...addContact(1, function(id) { console.log(id); });
Fenton

4
@ Steve Fenton: depois de ler sua resposta eu me perguntei por que eu pedi ... :-)
Veverke

1
A sintaxe da classe no ECMAScript não teria um =sinal ... em class myFuncs {vez de class myFuncs = {. Você também precisa estar executando em um ambiente que suporte a sintaxe da classe (nem todos os navegadores ainda o suportam). Se você ainda estiver com dificuldades, pode ser mais adequado para uma pergunta totalmente nova, pois seu problema não é sobre a passagem de funções - é a sintaxe geral do ES.
Fenton

338

Se você deseja passar uma função, basta referenciá-la pelo nome, sem os parênteses:

function foo(x) {
    alert(x);
}
function bar(func) {
    func("Hello World!");
}

//alerts "Hello World!"
bar(foo);

Mas, às vezes, você pode querer passar uma função com argumentos incluídos , mas não a chama até que o retorno de chamada seja chamado. Para fazer isso, ao chamá-lo, basta envolvê-lo em uma função anônima, assim:

function foo(x) {
   alert(x);
}
function bar(func) {
   func();
}

//alerts "Hello World!" (from within bar AFTER being passed)
bar(function(){ foo("Hello World!") });

Se preferir, você também pode usar a função aplicar e ter um terceiro parâmetro que é uma matriz de argumentos, como:

function eat(food1, food2)
{
    alert("I like to eat " + food1 + " and " + food2 );
}
function myFunc(callback, args)
{
    //do stuff
    //...
    //execute callback when finished
    callback.apply(this, args);
}

//alerts "I like to eat pickles and peanut butter"
myFunc(eat, ["pickles", "peanut butter"]); 

72
isso deve ser mais alto como ele também aborda como passar uma função com argumentos
deltanine

3
E algo que vou acrescentar a isto é que esse recurso só - sendo capaz de passar funções de JavaScript como argumentos ou variáveis ou similar - é o recurso que faz JavaScript tão poderoso e tão grande para código.
TheHansinator

@ Compynerd255 Eu concordo, isso e a capacidade de criar rapidamente literais de objetos são meus dois aspectos favoritos do Javascript. Eu sempre sinto falta de literais de objetos em linguagens que não os possuem.
dallin

3
Sou muito grato ao Javascript por fornecer esse recurso e a você @dallin por me informar que ele existe.
Dipendu Paul

No exemplo "envolvê-lo em uma função anônima", caso não seja óbvio, a função anônima retorna a própria função (com parâmetros) e é invocada quando atinge o parêntese em func (). Demorei um pouco para descobrir, então pensei que poderia ajudar os outros.
hubpixel

51

Exemplo 1:

funct("z", function (x) { return x; });

function funct(a, foo){
    foo(a) // this will return a
}

Exemplo 2:

function foodemo(value){
    return 'hello '+value;
}

function funct(a, foo){
    alert(foo(a));
}

//call funct    
funct('world!',foodemo); //=> 'hello world!'

Veja isso


no primeiro exemplo, você deve adicionar a palavra-chave return, assim: function funct (a, foo) {return foo (a) // isso retornará a} caso contrário você ficará indefinido
Artem Fedotov

34

Para passar a função como parâmetro, basta remover os suportes!

function ToBeCalled(){
  alert("I was called");
}

function iNeedParameter( paramFunc) {
   //it is a good idea to check if the parameter is actually not null
   //and that it is a function
   if (paramFunc && (typeof paramFunc == "function")) {
      paramFunc();   
   }
}

//this calls iNeedParameter and sends the other function to it
iNeedParameter(ToBeCalled); 

A idéia por trás disso é que uma função é bastante semelhante a uma variável. Em vez de escrever

function ToBeCalled() { /* something */ }

você também pode escrever

var ToBeCalledVariable = function () { /* something */ }

Existem pequenas diferenças entre os dois, mas de qualquer maneira - os dois são formas válidas de definir uma função. Agora, se você definir uma função e atribuí-la explicitamente a uma variável, parece bastante lógico que você pode passá-la como parâmetro para outra função e não precisa de colchetes:

anotherFunction(ToBeCalledVariable);

2
Basta typeof paramFunc == "function", pois se não for possível chamar, você poderá ignorá-lo.
Jimmy Knoot

16

Há uma frase entre os programadores de JavaScript: "Eval is Evil", então tente evitá-la a todo custo!

Além da resposta de Steve Fenton, você também pode passar funções diretamente.

function addContact(entity, refreshFn) {
    refreshFn();
}

function callAddContact() {
    addContact("entity", function() { DoThis(); });
}

8

Eu cortei todo o meu cabelo com esse problema. Como não consegui fazer os exemplos acima funcionarem, terminei assim:

function foo(blabla){
    var func = new Function(blabla);
    func();
}
// to call it, I just pass the js function I wanted as a string in the new one...
foo("alert('test')");

E isso está funcionando como um encanto ... pelo que eu precisava pelo menos. Espero que ajude alguns.


6

Sugiro colocar os parâmetros em uma matriz e depois dividi-los usando a .apply()função Portanto, agora podemos facilmente passar uma função com muitos parâmetros e executá-la de uma maneira simples.

function addContact(parameters, refreshCallback) {
    refreshCallback.apply(this, parameters);
}

function refreshContactList(int, int, string) {
    alert(int + int);
    console.log(string);
}

addContact([1,2,"str"], refreshContactList); //parameters should be putted in an array

5

Você também pode usar eval()para fazer a mesma coisa.

//A function to call
function needToBeCalled(p1, p2)
{
    alert(p1+"="+p2);
}

//A function where needToBeCalled passed as an argument with necessary params
//Here params is comma separated string
function callAnotherFunction(aFunction, params)
{
    eval(aFunction + "("+params+")");
}

//A function Call
callAnotherFunction("needToBeCalled", "10,20");

É isso aí. Eu também estava procurando por esta solução e tentei soluções fornecidas em outras respostas, mas finalmente consegui trabalhar com o exemplo acima.


2

Aqui está outra abordagem:

function a(first,second)    
{        
return (second)(first);           
}     

a('Hello',function(e){alert(e+ ' world!');}); //=> Hello world     

2

Na verdade, parece um pouco complicado, não é.

get método como um parâmetro:

 function JS_method(_callBack) { 

           _callBack("called");  

        }

Você pode fornecer como método de parâmetro:

    JS_method(function (d) {
           //Finally this will work.
           alert(d)
    });

1
Por favor, explique sua resposta.
precisa

2

As outras respostas fazem um excelente trabalho descrevendo o que está acontecendo, mas uma "pegadinha" importante é garantir que tudo o que você passar seja realmente uma referência a uma função.

Por exemplo, se você passar por uma string em vez de uma função, receberá um erro:

function function1(my_function_parameter){
    my_function_parameter();   
}

function function2(){
 alert('Hello world');   
}

function1(function2); //This will work

function1("function2"); //This breaks!

Veja JsFiddle


0

Em algum momento em que você precisa lidar com o manipulador de eventos, também precisa passar o evento como argumento, a maioria das bibliotecas modernas como react, angular pode precisar disso.

Preciso substituir a função OnSubmit (função da biblioteca de terceiros) por alguma validação personalizada no reactjs e passei a função e o evento, como abaixo

ORIGINALMENTE

    <button className="img-submit" type="button"  onClick=
 {onSubmit}>Upload Image</button>

FEITO UMA NOVA FUNÇÃO uploade chamado passou onSubmite evento como argumentos

<button className="img-submit" type="button"  onClick={this.upload.bind(this,event,onSubmit)}>Upload Image</button>

upload(event,fn){
  //custom codes are done here
  fn(event);
}

-3

Você também pode usar um JSON para armazenar e enviar funções JS.

Verifique o seguinte:

var myJSON = 
{
    "myFunc1" : function (){
        alert("a");
    }, 
    "myFunc2" : function (functionParameter){
        functionParameter();
    }
}



function main(){
    myJSON.myFunc2(myJSON.myFunc1);
}

Isso imprimirá 'a'.

O seguinte tem o mesmo efeito com o acima:

var myFunc1 = function (){
    alert('a');
}

var myFunc2 = function (functionParameter){
    functionParameter();
}

function main(){
    myFunc2(myFunc1);
}

O que também tem o mesmo efeito com o seguinte:

function myFunc1(){
    alert('a');
}


function myFunc2 (functionParameter){
    functionParameter();
}

function main(){
    myFunc2(myFunc1);
}

E um paradigma de objeto usando Class como protótipo de objeto:

function Class(){
    this.myFunc1 =  function(msg){
        alert(msg);
    }

    this.myFunc2 = function(callBackParameter){
        callBackParameter('message');
    }
}


function main(){    
    var myClass = new Class();  
    myClass.myFunc2(myClass.myFunc1);
}

9
Seu exemplo de código principal não é JSON. Este é um objeto JavaScript padrão. Você também não pode enviar / receber funções em JSON.
Matthew Layton
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.