How does jQuery make the parameters to be dynamic?

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);

Notice that the first example, the fadeOut function, receives a closure as the first argument. The second gets a Number in the first argument and a closure, in the second. Third party receives Number .

Notice that all three cases work normally.

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

How could I do the same operation in a function I created?

Example:

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

I want it to be possible to do this:

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
    
asked by anonymous 18.10.2015 / 23:59

4 answers

11

"First and foremost, thank you for mental exercise."

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

At 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, is the function responsible for the sum, being:

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

When creating functions of this type, you should note that javascript functions:

  • Do not specify the data type for the arguments.
  • Do not check what types of arguments have been passed.
  • Do not check number of arguments received.

Default unsigned:

  • If the function is called with fewer arguments than the declared ones, the missing elements are defined for: 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 goes return 0 as sum value.

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

Usage example:

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 arguments to pass the parameters, such as the @bfavarreto example, or as @Guilherme Lautert suggested, also dynamize the order of functions:

  

Using the sort () method, you can create the same effect by placing the function always in the last position, making the function take its normal course.

<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 arguments object is similar to an array, but is not an array, the only array property that it has is the (length) size. Using the slice method on it, prevents Javascript engine optimizations. It is recommended to create an array by iterating the arguments object.

    
19.10.2015 / 07:38
12

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

A simple example that seems to fit 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 find this an elegant way of handling such cases. You do not name any function arguments, only check if the latter is a callback, and act according to what you received. If the latter is a callback, execute this callback, passing it on to the other arguments received (the apply does this by running the function with N arguments passed as array).

    
20.10.2015 / 22:43
7

This is called function overloading .

Strictly, there is no overloading in JavaScript since it is allowed to pass any number of parameters of any type to the functions. But this is often simulated by checking how many types of arguments were passed and what type they are. The implementation depends on the type signatures .

The example function you gave would look something 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 therefore has two signatures:

sum_with_callback(intA [, intB] [, closure])
sum_with_callback(intA [, closure])
    
19.10.2015 / 06:42
3

Just adding a more complex example to @bfavaretto's answer .

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');
    
21.10.2015 / 15:58