Javascript Method Overload Function

Asked

Viewed 857 times

4

I’m reading a book called "Secrets of Ninja Javascript" and in it, I came across a method overload function. So far so good, I understood what she does, I understood that she 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 on your console the method ninjas.find() it will return the full Array of the property values of the object ninjas, if you call something like ninjas.find("Dean"), only the name corresponding to the query will be returned.

My question is this: When we do the second assignment of the function find by means of addMethod(), Why doesn’t she overwrite the previous one? Or rather, I want to understand what is happening so that the two functions find may exist simultaneously.

  • I think you have overwritten (with addMethod) the method without parameters. When you call the method with parameters, the called method is what already existed not what you injected.

  • Yes, but if after calling the method with parameters you call the no parameters, it is still there

3 answers

6


To eduardo’s response explains what is happening, and I will try to illustrate. Each time addMethod is called, she keeps as old whatever is in object[name], creates a new function, and stores it in place of the old one. And at each call the addMethod creates a new old, accessible only by the function that the method creates next.

Clearing the code makes it 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 a old different:

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

Note that there is a chain of access there. And the function created in addMethod takes advantage of this, it decides whether to call the function passed or the old depending on 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 A have no arguments, B have a and C 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 obj.nomeDoMetodo(), 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 a addMethod, 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 arrived at 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
  • So he does layered processing until he finds one that’s suitable, right? Just one more doubt, as it invokes more than once the function, that is to say, as the if is evaluated more than once without the use of a for?

  • 1

    It is more or less "layered". It is not the same function being called multiple times, it is multiple "copies" function. Each copy has different values in fn and old. When you call the new added method, the function or use the fn or passes to the old, which may also use its own fn, or call your own old, and so on. It’s clearer? @Lucasmuller

  • Yes, thank you very much.

  • Great explanation, everything is better with examples :D

  • Could you just help me with a question? I believe that this practice then is bad, because it creates "copies" of the object, which could become costly for memory, this correct?

  • @Guilhermelautert You can even avoid the creation of copies. I thought of a way via a chain of prototypes, but I won’t go into details. Whether this will become costly in terms of memory depends heavily on the use case. So I wouldn’t dismiss this solution as "bad practice" just because of this, it has to do case by case.

Show 1 more comment

4

I think I found the explanation here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

Let’s see if I can explain.

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

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

Thus, the old variable in the context of the Object[name] function will have value when the find function is executed allowing that IF which of the run versions works, since the old variable will have value.

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

  • 1

    That’s exactly what’s happening, +1.

  • 1

    The interesting thing is that it is a chain of closures. Each function has a old different.

  • I still do not fully understand but already clarified a lot, I will wait a little longer before accepting the answer, but thank you for the explanation.

  • Taking advantage, this MDN site for javascript is very good. I do not know how is his translation into Portuguese. But it’s a great reference to study how Javascript works.

0

Guys, I’m posting as an answer but I just want to show through the image below the concept of closures cited in previous answers.

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

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

Within that old there was a new closure, in that closure there was one more fn and one more old. To fn with the function of 1 parameter and the old with one more closure.

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

This shows that every time the addMethod spun he created a new scope, a new closure, and that’s why the code runs in layers.

For if the condition of the parameters is satisfied it rotates the fn layer (closure) which is otherwise old, entering a new layer in which again will be evaluated the condition and so on.

Closures criadas na execução do código

Browser other questions tagged

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