How does jQuery make parameters dynamic?

Asked

Viewed 489 times

12

I noticed that in jQuery, parameters can often be passed or omitted, and regardless of order, they work as expected.

What I mean is this. See the following codes below:

 $('#x').fadeOut(function(){ alert('complete') }); 
 $('#x').fadeOut(400, function(){ alert('complete') });
 $('#x').fadeOut(400);

Note that the first example, the fadeOut function, takes a closure as the first argument. The second, takes a Number in the first argument and a closure in the second. The third receives a Number.

Note that the three cases works normally.

But if a function is declared to accept a certain value in the first parameter, how can jQuery "modify it" at runtime?

How could I have the same operation on a function that I created?

Example:

function sum_with_callback(intA, intB, closure)
{
    // Faz a mágica aqui
}

I want it to be possible to do so:

sum_with_callback(1, 2, function (){});

sum_with_callback(1, function(){}); // Quero que o intB seja 0, sem passá-lo

sum_with_callback(1, 2); // Sem o callback, a função tem que funcionar
  • 2

    Related (almost? dup...): http://answall.com/questions/4293/como-crea-uma-fun%C3%A7%C3%A3o-in-javascript-that-accepts-an-n%C3%Bamero-arbitr%C3%A1rio-de-argument

4 answers

12

You do not need to name any function parameter. When it is invoked, all received arguments are available in the object arguments. You can examine what is there and treat it the way it is most convenient.

A simple example that seems to meet your requirements, with support for two or more arguments:

function f() {
    // não faz nada se não tiver recebido 2+ argumentos (opcional)
    if(arguments.length < 2) return;

    // converte arguments em array (mais versátil)
    var args = Array.prototype.slice.call(arguments);

    // só verificando o que recebemos
    console.log('f recebeu ' + args);

    // chegou uma função no fim? 
    // usa o pop para tirar essa função de args;
    // se não, cria callback vazio (dá pra melhorar isso)
    var callback = typeof args[args.length-1] == 'function' ? args.pop() : function(){};
 
    // chama a função com o restante dos argumentos
    callback.apply(null, args);
}

// Testando
f(1, 2, 3, function(){
    console.log(arguments);
}); 

f(1, function(){
    console.log(arguments);
}); 

f(1, 2);

I think that’s an elegant way to handle these cases. You do not name any argument from the function, just check if the latter is a callback, and act accordingly. If the latter is a callback, execute this callback, relaying to it the other arguments received (the apply does this by running the function with N arguments passed as array).

  • Hello, as I already said, JS is not my strong suit. In case you get 0, or 1 argument this function works ? Or it only works with 2 or more arguments ?

  • The only thing that limits to 2+ arguments is the first line. If you withdraw, it works for any amount. I narrowed it down to 2+ because the question seemed to ask that.

  • +1 very intelligent internal coding

  • +1, I did a test now, and it’s really practical. I didn’t know the object Arguments in the js, I knew, but I ignored its use, maybe because I was not exploring the area, I saw this example you just wrote, and also the original in the firefox page, so I could understand better, I will now try to rewrite the above function using the object Arguments and the other methods.

11


"First of all, thank you for the mental exercise".

At first it was complicated, because of logic, since it had to set to false whenever it signed a new value, or even if it left parameters missing.

In the end I got this here:

<script>
function bar(){
    //foo(4); // 4
    //foo(4, 4) // 8
    //foo(4, function(soma){alert(soma);}) // alert(4)
    foo(4, 4, function(soma){alert(soma);}) // alert(8)
}

function foo(argA, argB, callback){
    var soma;
    var argA = (typeof argA !== 'undefined') ? argA : 0;
    var argB = (typeof argB !== 'undefined') ? argB : 0;
    var callback = (typeof callback !== 'undefined') ? callback : false;

    if(argA && argB && callback === false){
       if(typeof argB === "function"){
          callback = argB;
          soma = argA;
       } else {
          soma = argA + argB;
       }
    } else if(argA && argB && callback) {
      soma = argA + argB;
    } else {
      soma = argA;
    }


    if(callback && typeof callback === "function"){
        return callback(soma);
    } else {
        return soma;
    }
}
</script>

The function foo in this case, it is the function responsible for the sum, being:

foo(arg1,[arg2],[callback]) { // magia }

When creating functions of this type, you should take into account that javascript functions:

  • Do not specify data type for arguments.
  • Do not verify which types of arguments have been passed.
  • No verification of number of arguments received.

Default of the unsigned:

  • If the function is called with fewer arguments than the declared ones, the missing elements are set to : Undefined.

The first parameter is required, or the function fails, in this example I did not create conditions in case the first parameter was not provided, so instead of returning false if no parameter is given, the function will return 0 as the sum.

The JS is not my strong suit, however, I think the function is well reduced and the logic is correct.

Example of use:

function bar(){
    //foo(4); // 4
    //foo(4, 4) // 8
    //foo(4, function(soma){alert(soma);}) // alert(4)
    foo(4, 4, function(soma){alert(soma);}) // alert(8)
}

function foo(argA, argB, callback){
    var soma;
    var div = document.getElementById('demo');
    var argA = (typeof argA !== 'undefined') ? argA : 0;
    var argB = (typeof argB !== 'undefined') ? argB : 0;
    var callback = (typeof callback !== 'undefined') ? callback : false;

    if(argA && argB && callback === false){
       if(typeof argB === "function"){
          callback = argB;
          soma = argA;
       } else {
          soma = argA + argB;
       }
    } else if(argA && argB && callback) {
      soma = argA + argB;
    } else {
      soma = argA;
    }
    
    
    if(callback && typeof callback === "function"){
    	return callback(soma);
    } else {
        //return soma;
        div.innerHTML = soma;
    }
}
<body>

<button id="bt" onclick="bar();">Clica-me</button>   

<p id="demo"></p>

</body>

Below the same function, for the same purpose, using the object Arguments to pass the parameters, such as the @bfavarreto example, or as @Guilherme Lautert suggested, also streamline the order of functions:

Using the method Sort(), can create the same effect by placing the function always in the last position, making the function has its normal stroke.

<script>
  function foo(){
  	var div = document.getElementById('sum');
  	div.innerHTML = soma(2,3,4,3); // Retorna: 8
  	//return soma(2,2,function(i){alert(i);}); // Retorna: alert(4)
  	//return soma(); // Retorna: 0
        //return soma(1,function(i){alert(i);},5); // Retorna: alert(6)
  	//return soma(1,5, function(i){alert(i);}); // Retorna: alert(6)
  }

  function soma(){
  	var array = Array.prototype.slice.call(arguments);
  	var len = arguments.length;
  	//console.log(array.length); // Retorno: (int)N argumentos contando com o callback;
        var array = array.sort(); // utilizando o método sort() a função vai estar sempre na última posicao
  	var callback = typeof array[len-1] == 'function' ? array.pop() : false;
  	var Newlen = array.length;
  	var soma = 0;
  	// Somar os valores
  	for(i=0; i<Newlen;++i){
  		soma += Number(array[i]);
  	}
  	//console.log(array.length); // Retorno: (int)N argumentos sem o callback;
  	
    //Controlar o retorno
  	if(callback){
  		// Com callback
  		return callback(soma);
  	} else {
  		// Sem callback
  		return soma;
  	}
  	
  }
  </script>
<body>
  <div id="sum"></div>
  <button onclick="foo();">Somar</button>
 </body>

The object Arguments is similar to an array, but is not an array, the only array property it has is the size (length). Using the method Slice in it, prevents optimizations in Javascript engines. It is recommended to create an array iterating the object Arguments.

7

This is called Function overloading.

Strictly speaking, there is no overloading in Javascript since it is allowed to pass any number of parameters of any kind to the functions. But this is often simulated by checking how many types of arguments have been passed and what type they are. The implementation depends on the type Signatures desired.

The function you gave of example would be more or less like this:

function sum_with_callback(intA, intB, closure) {

    if (typeof intB != 'number')
        if (typeof intB == 'function')
            closure = intB;
        intB = 0; // "argumento padrão";

    var resultado = intA + intB;

    if (typeof closure != 'undefined')
        // executa o callback se ele tiver sido passado
        return closure(resultado);

    return resultado;
}

The function is therefore with two Signatures:

sum_with_callback(intA [, intB] [, closure])
sum_with_callback(intA [, closure])

3

Just adding a more complex example to reply from @bfavaretto.

function f(){

    var n = null; // name
    var t = null; // timer
    var c = null; // function

    for (var i = 0; i < arguments.length; i++){
        if(n == null && typeof(arguments[i]) == 'string'){
            n = arguments[i];
        }

        if(t == null && typeof(arguments[i]) == 'number'){
            t = arguments[i];
        }

        if(c == null && typeof(arguments[i]) == 'function'){
            c = arguments[i];
        }
    }

    t = (t == null) ? 1000 : t;
    n = (n == null) ? 'default' : n;
    c = (c == null) ? function(){ alert('callback Default')} : c;

    setTimeout(function(){
        alert(n);
        c();
    }, t);
}

f();
f('Guilherme', 2000, function(){ alert('callback1')});
f(function(){ alert('callback2')}, 'Guilherme', 4000);
f(6000, function(){ alert('callback3')}, 'Guilherme');
  • The @bfavaretto reply practically says you can sign n arguments, no matter how many they are, as long as they have a callback at the end, using the object Arguments, What you have there is something else. I’ll edit my answer in a little while and add an example based on Arguments, so you can see.

  • Yes, in his if you have n arguments, the one I put accepts n arguments, however they can be disordered and only the 1 of each type is assigned to its respective function.

  • +1, What is the usefulness of this ? Create effects ?

  • Actually in this example I just wanted to demonstrate how to catch disordered elements and that generate the same effect. This function by itself has no effect, however if you want I can change with similar use that has some purpose.

  • No, I know how it works, thanks anyway.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.