【问题标题】:Understanding this JavaScript function overloading example了解这个 JavaScript 函数重载示例
【发布时间】:2013-08-09 22:28:32
【问题描述】:

我目前正在通过John Resig 学习Secrets of the JavaScript Ninja,我希望有人可以帮助我进一步理解其中一个示例。

它是一个允许对对象进行方法重载的函数,每个重载都有自己的定义和行为。他在博客上写了here

代码如下所示:

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

并像这样使用:

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

我想我了解它的大部分工作原理,但您可以得到比我从上面的博客文章中给出的更好的解释。

但是,它使用闭包访问oldfn 的值,我还在研究。

编辑 - 在下方添加 jsFiddle

当试图理解它时,我意识到 return fn.apply(this, arguments) 行可能只是 return fn() ,结果似乎相同。请参阅此 jsFiddle 中的示例。

那么,如果不需要,为什么要使用apply 语法?

我尝试在不应用的情况下使用jsFiddle 中的示例,但似乎总是不行

此外,当我们返回这些函数时究竟发生了什么,尤其是在以下情况下:

return old.apply(this, arguments);

我真的很想深入了解如何使用这种方法,以及它为什么有效,因此任何见解都将不胜感激。

谢谢

【问题讨论】:

  • 感谢大家的所有回答。我会全部研究并接受最佳答案——不过,看起来似乎很难看到高质量的答案:)

标签: javascript closures


【解决方案1】:

那么,如果不需要,为什么要使用apply 语法?

实际使用需要。

thisarguments 对于每个 function 都是不同的,并且在调用它们时设置。通过使用fn(),将使用空的arguments 集合或没有为this 传递值来调用fn

.apply(this, arguments) 调用fnold 并传递来自当前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

此外,当我们返回这些函数时究竟发生了什么,尤其是在以下情况下:

return old.apply(this, arguments);

好吧,addMethod 的目的是创建一个函数链,每个函数都知道并可以调用在它之前创建的 old 函数。

对于书中的例子,链是这样构建的:

// 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

每个function(){...} 都是通过在不同的作用域/闭包中重新评估相同的表达式而创建的唯一实例:

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

然后每个.apply() 跟随一个“手臂”或“箭头”到达oldfnreturns 允许将结果通过 / 反向传递。

【讨论】:

  • 取决于strict modeUsing fn() would call without arguments or a this value 为假。 this 将是 windowundefined(参见 this
  • @xanatos 这不是假的,只是不完整。 thispassed 值是 undefined,尽管它可能被替换或 默认 为全局对象。我试图专注于传递的值,但可能不应该忽略它。
  • 感谢非常详细的回答和 cmets。我已经在 jsFiddle 中尝试了五个重载而不使用 apply,并且每次使用 fn() 都可以调用正确的函数。这让我的理解感到困惑。
  • @davy 使用fn() 仍将调用预期的函数,但它不会将有关原始调用的任何信息传递给ninja.test()。由于您在另一部分中使用old.apply(...),这些函数将传递该信息,这就是console.log(argument) 看起来正确的原因。但是,请尝试在方法中记录ab 等:jsfiddle.net/n89ec。然后,使用.apply() 重试:jsfiddle.net/5KryU
  • @Jonathan 最后一分钱掉了。谢谢。
【解决方案2】:

这里是代码的细分。

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

了解apply 的工作原理很重要。它在函数中为this 设置上下文。例如:

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

示例 http://jsfiddle.net/NCaKX/

【讨论】:

  • 感谢您提供如此详细的回答。
【解决方案3】:

我认为你错过了.apply 的重点

我不会解释它:P 你可以找到很多很好的解释,例如:

tl;博士

.apply 允许您设置this 上下文。 .apply 允许您将参数作为数组传递,允许可变数量的参数。

【讨论】:

    【解决方案4】:

    在 Javascript 中,this 变量不是在函数声明时设置,而是在执行时设置,具体取决于访问函数引用的对象。

    你需要使用apply,不仅是因为前面提到的this的动态绑定,还因为你事先不知道有多少个参数;所以apply 接收参数列表并将列表中的每个元素作为单独的参数传递给函数。

    【讨论】:

      【解决方案5】:

      使用.apply可以指定上下文

      window.name = "window";
      
      var object = {
          name:"object"
      };
      
      function showName(){
          alert(this.name);
      }
      
      showName(); // shows window
      showName.apply(object); // shows object
      

      更多:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

      【讨论】:

        【解决方案6】:
        fn.apply(this, arguments)
        

        this 作为当前thisarguments 作为参数调用fn

        fn()
        

        调用fnthis 等于windowundefined(取决于您是处于“非严格模式”还是“严格模式”)但没有参数。

        在这个Mozilla.org 页面中,有一个很好的关于this 的讨论(总是有用的)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-18
          • 1970-01-01
          • 1970-01-01
          • 2011-11-11
          • 1970-01-01
          相关资源
          最近更新 更多