最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

closures - Understanding this JavaScript function overloading example - Stack Overflow

programmeradmin3浏览0评论

I'm currently studying Secrets of the JavaScript Ninja by John Resig and I'm hoping someone can help me further understand one of the examples.

It is a function that allows method overloading on an object, each overload has it's own definition and behaviour. He blogged about it here.

The code looks like this:

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

And used like this:

addMethod(obj,'funcName',function(){});
addMethod(obj,'funcName',function(a){});
addMethod(obj,'funcName',function(a,b){});

I think I understand most of how this works but you can get a better explanation than I can give from the blog post above).

However, it accesses the value of old and fn using closures, which I'm still studying.

EDIT - added jsFiddle below.

When trying to understand it, I realised that the line return fn.apply(this, arguments) could be simply return fn() with what seems to be the same result. See an example in this jsFiddle.

So, Why is it using the apply syntax if not required?

I have tried playing with the example in jsFiddle without apply and it always seems to wo

Also, what exactly is happening when we return those functions, especially in the case of:

return old.apply(this, arguments);

I really want to get a solid understanding of not just how to use this method but why it works so any insight would be greatly appreciated.

Thanks

I'm currently studying Secrets of the JavaScript Ninja by John Resig and I'm hoping someone can help me further understand one of the examples.

It is a function that allows method overloading on an object, each overload has it's own definition and behaviour. He blogged about it here.

The code looks like this:

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

And used like this:

addMethod(obj,'funcName',function(){});
addMethod(obj,'funcName',function(a){});
addMethod(obj,'funcName',function(a,b){});

I think I understand most of how this works but you can get a better explanation than I can give from the blog post above).

However, it accesses the value of old and fn using closures, which I'm still studying.

EDIT - added jsFiddle below.

When trying to understand it, I realised that the line return fn.apply(this, arguments) could be simply return fn() with what seems to be the same result. See an example in this jsFiddle.

So, Why is it using the apply syntax if not required?

I have tried playing with the example in jsFiddle without apply and it always seems to wo

Also, what exactly is happening when we return those functions, especially in the case of:

return old.apply(this, arguments);

I really want to get a solid understanding of not just how to use this method but why it works so any insight would be greatly appreciated.

Thanks

Share Improve this question edited Aug 8, 2013 at 11:08 davy asked Aug 8, 2013 at 9:12 davydavy 4,58210 gold badges50 silver badges73 bronze badges 1
  • Thanks everyone for all the answers. I will study them all and accept the best answer - although, that looks as if it may be difficult looking at the high quality of the answers :) – davy Commented Aug 8, 2013 at 11:04
Add a ment  | 

6 Answers 6

Reset to default 3

So, Why is it using the apply syntax if not required?

It is actually required for the usage.

this and arguments are different for every function and are set when they're called. By using fn(), fn will be called with an empty arguments collection or no value passed for this.

.apply(this, arguments) calls fn or old and passes along the values for both from the current function.

var obj = {};

addMethod(obj, 'funcName', function (a, b) {
    console.log(this === obj);
    console.log(a, b);
    console.log(arguments[0], arguments[1]);
});

obj.funcName(2, 3);
// true
// 2, 3
// 2, 3

Also, what exactly is happening when we return those functions, especially in the case of:

return old.apply(this, arguments);

Well, the purpose of addMethod is to create a chain of functions where each knows about and can call the old function created before it.

For the example from the book, the chain is built as:

// after: addMethod(obj, 'funcName', function(){});
obj.funcName = function(){...} ──> function(){}
// after: addMethod(obj, 'funcName', function(a){});
obj.funcName = function(){...} ──────> function(a){}
               └── function(){...} ──> function(){}
// after: addMethod(obj, 'funcName', function(a,b){});
obj.funcName = function(){...} ──────────> function(a,b){}
               └── function(){...} ──────> function(a){}
                   └── function(){...} ──> function(){}
Legend:
  `└──` represents an `old` reference
  `──>` represents a `fn` reference

Each function(){...} is a unique instance created by reevaluating the same expression in a different scope/closure:

function(){
  if (fn.length == arguments.length)
     return fn.apply(this, arguments)
  else if (typeof old == 'function')
     return old.apply(this, arguments);
}

Each .apply() then follows an "arm" or "arrow" to either an old or fn and the returns allow the result to be passed back through / in reverse.

Here is a breakdown of the code.

function addMethod(object, name, fn) {

   //get the old function from the object
   var old = object[name];

   //assign a new function to the property
   object[name] = function(){

      //See if the method signatures match, if they do execute the new method
      if (fn.length == arguments.length)

        /*
         Call the function provided using apply, the first argument "this" is the object
         it sets the context for this in the function we provide, second argument is
         the arguments provided to the function.  We must return the result of the 
         function.    

        */

         return fn.apply(this, arguments)

      //If the old property on the object is a function and the new functions signature
      //did not match call the old function assigned to the property.

      else if (typeof old == 'function')

         // Same as before calling with apply, setting context and returning result

         return old.apply(this, arguments);
};

It is important to understand how apply works. It sets the context for this within a function. For example:

var myFunction = function(){
   alert(this.msg);  //this will be set by what apply passes in as the first arg
};

var obj1 = {msg: "Hello"};
var obj2 = {msg: "World"};

myFunction.apply(obj1);
myFunction.apply(obj2);

Example http://jsfiddle/NCaKX/

I think you're missing the point of .apply

I wont try to explain it :P you can find many good explanations, such as:

  • What is the difference between call and apply?
  • Why invoke "apply" instead of calling function directly?

tl;dr

.apply allows you set the this context. .apply allows you to pass arguments as an array, allowing for a variable number of arguments.

In Javascript the this variable is not set in the moment of declaration of the function, but in the moment of execution, depending on which object was used to access the function reference.

You need to use apply not only because of the aforementioned dynamic bind of this, but also because you don't know how many arguments are there before hand; so apply receives a list of arguments and passes each element of the list as an individual argument to the function.

Using .apply the context can be specified

window.name = "window";

var object = {
    name:"object"
};

function showName(){
    alert(this.name);
}

showName(); // shows window
showName.apply(object); // shows object

more here : https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Operators/this

fn.apply(this, arguments)

calls the fn with this as the current this and arguments as parameters.

fn()

calls the fn with this equal to window or undefined (depending if you are in "non strict mode" or in "strict mode") but without parameters.

In this Mozilla page there is a good discussion of this (always useful)

发布评论

评论列表(0)

  1. 暂无评论