JavaScript Method Overload Function

4

I'm reading a book called "Secrets of the JavaScript Ninja" and in it, I came across a method overload function. So far so good, I understood what it does, I understand that it makes depending on the amount of parameters passed to the function a different method will be called.

The function that appears in the book is as follows:

function addMethod(object, name, fn){
  var old = object[name];
  object[name]=function(){
    if(fn.length == arguments.length)
      return fn.apply(this, arguments)
    else if (typeof old == 'function')
      return old.apply(this, arguments);
  };
}

var ninjas = {
  values : ["Dean Edwards", "Sam Stephenson", "Alex Russel"]
};

addMethod(ninjas, "find", function(){
  return this.values;
});

addMethod(ninjas, "find", function(name){
  var ret = [];
  for (var i =0; i < this.values.length; i++)
    if(this.values[i].indexOf(name) == 0)
      ret.push(this.values[i]);
  return ret;
});

If you call in the console the ninjas.find() method it will return the complete Array of the values property of the ninjas object, if it calls something like ninjas.find("Dean") , only the name corresponding to the search will be returned. >

My question is this: When we make the second assignment of the function find through addMethod() , why does not it overwrite the previous one? Or rather, I want to understand what is happening so that the two find functions can exist simultaneously.

    
asked by anonymous 02.10.2015 / 18:22

3 answers

6

The Eduardo's answer explains what's happening, and I'll try to illustrate. Each time addMethod is called, it stores as old what is object[name] , creates a new function, and saves in place of the old one. And with each call the addMethod creates a new old , accessible only by the function that the method creates next .

Clearing the code, it becomes clearer:

function addMethod(object, name, fn){
  var old = object[name];
  object[name] = function(){
    // ...
  };
}

For example, if you have 3 functions, A , B and C , and pass one at a time, you create 3 new functions, each with access to% p>

addMethod(obj, "nomeDoMetodo", A); // old é undefined
addMethod(obj, "nomeDoMetodo", B); // old é uma closure com acesso a A
addMethod(obj, "nomeDoMetodo", C); // old é uma closure com acesso a B

See that there is a chain of accesses there. And the function created in old takes advantage of this, it decides whether to call the function passed or to old according to the number of arguments:

if(fn.length == arguments.length)
  return fn.apply(this, arguments)   // chama a função passada, fn

else if (typeof old == 'function')
  return old.apply(this, arguments); // chama a função em old, que tem
                                     // acesso ao que foi passado
                                     // na chamada anterior

Suppose that addMethod has no arguments, A has one and B has two:

function A() {}
function B(um) {}
function C(um, dois) {}

addMethod(obj, "nomeDoMetodo", A); // Chamada #1
addMethod(obj, "nomeDoMetodo", B); // Chamada #2
addMethod(obj, "nomeDoMetodo", C); // Chamada #3

// ATENÇÃO: chamada sem nenhum argumento
obj.nomeDoMetodo();

What happens when we call C , with no arguments? Let's see line by line.

// Código da função criada na Chamada #3 a addMethod
if(fn.length == arguments.length)    // false: fn da Chamada #3 é
                                     // C, que tem 2 argumentos, e agora
                                     // nenhum foi passado
  return fn.apply(this, arguments)   // (não executa)

else if (typeof old == 'function')   // cai aqui
  return old.apply(this, arguments); // chama a função em old, que tem
                                     // acesso a B

Then an equal function, but created in Call # 2 at obj.nomeDoMetodo() , is invoked:

// Código da função criada na Chamada #2 a addMethod
if(fn.length == arguments.length)    // false: fn da Chamada #2 é
                                     // B, que tem 1 argumento, e agora
                                     // nenhum foi passado
  return fn.apply(this, arguments)   // (não executa)

else if (typeof old == 'function')   // cai aqui
  return old.apply(this, arguments); // chama a função em old, que tem
                                     // acesso a A

And we came to the function created in Call # 1:

// Código da função criada na Chamada #1 a addMethod
if(fn.length == arguments.length)    // true: fn da Chamada #1 é
                                     // A, que não tem argumentos.
  return fn.apply(this, arguments)   // Executa A
    
02.10.2015 / 20:40
4

I think I found the explanation here: link

Let's see if I can explain.

The addMethod () function is assigning a function to the object variable, which is a pointer (modifications made to object within addObject will persist in the final object).

This makes the object [name] function a closure () that in javascript is a special object that holds two things: the function itself and the environment where it was created.

So, the old variable in the context of the object [name] function will have value when the find function is executed, allowing that IF which tests which of the versions to execute, since the old variable will have value.

Does that make any sense? That's what I understood.

    
02.10.2015 / 19:02
0

Well, I'm posting this as an answer but I just want to show you the concept of closures quoted in the previous answers.

Obs. I created an extra function with three parameters to make it clearer.

I simply circled the code on my console and then typed ninjas to see the object and everything in it. So you can see that it has two properties only: values and find (actually a method). So far no news, but then I went to see what I had in this find , and there I found a scope and inside it a closure and in it my two variables created by addMethod() , fn and old . In fn was my function of 2 parameters and in old had another scope.

Within this old there was a new closure , in that closure there was a fn and a old . A% with_function with 1-parameter function and% with_with% plus_with%.

In this new fn there was also a variable old and a closure , closure was the function without parameters and fn was undefined.

This shows that every time old has rolled it has created a new scope, a new fn , which is why the code runs in layers.

If the condition of the parameters is satisfied, it runs the old of the layer ( addMethod ) that is, otherwise it runs the closure , entering a new layer in which the condition will be evaluated again and thus on.

    
05.10.2015 / 18:54