无法理解这个Javascript函数(函数重载)

时间:2015-06-22 20:52:04

标签: javascript function methods overloading

我正在阅读Javascript Ninja的秘密,并且遇到了一个我无法完全理解的例子。这个例子之前被其他一些用户引用过,但他们的疑惑与我的不同。这是一个例子:

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 ninja = {};
addMethod(ninja,'whatever',function(){ /* do something */ });
addMethod(ninja,'whatever',function(a){ /* do something else */ });
addMethod(ninja,'whatever',function(a,b){ /* yet something else */ });

我对范围,结束和apply()的使用有深刻的理解。我的怀疑是:

  • fn.length将返回fn中定义的参数数量。 arguments.length会返回哪些参数的数量?已经存在的功能?
  • 但如果是这样,并且它们匹配,为什么它会应用新功能而不是现有功能?
  • 如果arguments.length返回给定函数的编号,那么它们何时会不同?
  • 我添加了10个方法,从没有参数开始并且每次增加它们的数量,在添加第10个方法之后,我调用没有参数的方法,它在哪里存储'第一个功能?
  • 我不明白在这里使用

我可能没有一些能够回答所有问题的关键概念。请随意给我一个解释,而不是单独回答问题。

5 个答案:

答案 0 :(得分:2)

  

fn.length将返回fn中定义的参数数量。 arguments.length会返回哪些参数的数量?现有的功能是什么?

没有。 arguments是一个类似于数组的局部变量,可以在函数内部使用。它包含传递给函数的参数数量。

通过解决这个问题可以回答你的其他问题:

  

我添加了10个方法,从没有参数开始并且每次增加它们的数量,在添加第10个方法之后,我调用没有参数的方法,它在哪里“存储”第一个函数?

一次一步查看addMethod方法可能会有所帮助:

function addMethod(object, name, fn) {

  var old = object[name];
  // Get the old function corresponding to this name. Will be "undefined"
  // the first time "addMethod" is called.


  object[name] = function(){
  // Now, assign object[name] to a new function.
  // The critical part of this function is that "old" is captured inside of
  // this function and will be available any time the function is called.

       if (fn.length == arguments.length)
       // if the number of parameters belonging to the function we've added
       // matches what was passed in, call "fn"
          return fn.apply(this, arguments)

       else if (typeof old == 'function')
       // Otherwise if there's another function with this name
       // call it instead.
          return old.apply(this, arguments);

    };
}

因此,让我们在您的示例中对addMethod进行每次调用,并检查fnold的值。您可以考虑将这种方式组织为彼此叠加的函数的方式,并利用old的范围。

addMethod(ninja,'whatever',function(){ /* do something */ });
// old === undefined

addMethod(ninja,'whatever',function(a){ /* do something else */ });
// old === function #1

addMethod(ninja,'whatever',function(a,b){ /* yet something else */ });
// old === function #2

在调用addMethod结束时,三次ninja.whatever引用一个函数,如果可能的话调用函数#3,然后调用old函数(函数#2)不匹配函数#3的参数列表长度。

在视觉上,你可以这样想:

function #3 (a,b)
function #2 (a)
function #1 ()

每个函数的old引用指向其下面的函数。

现在让我们来看看你致电ninja.whatever()时会发生什么。这个函数位于我们堆栈的“底部”。

  1. 当前与whatever关联的功能是功能#3。调用函数#3时,fn.length != arguments.length,执行old(函数#2)。
  2. 所以现在我们正在执行#2功能。同样,在调用函数#2时,执行fn.length != arguments.lengthold(函数#1)。
  3. 最后,执行功能#1。这次fn.length == arguments.length,因此调用该函数。

答案 1 :(得分:1)

评论补充:

//replace object[name] with a wrapper that either calls the passed-in 
//function (fn) or the old value of object[name]
function addMethod(object, name, fn) {

  //store object[name]    
  var old = object[name];



  object[name] = function(){

        //if the wrapper is called with as many arguments as is the arity of the passed in function (fn), call the passed in function (fn)
       if (fn.length == arguments.length)

          return fn.apply(this, arguments)

       //otherwise call the old value of object[name] but only if it is a function
       else if (typeof old == 'function')

          return old.apply(this, arguments);

    };
}

答案 2 :(得分:1)

创建函数时,它会保留对定义范围内变量的访问权限。在这种情况下,我们创建的每个方法都可以访问带有变量old,object,name和fn的作用域。旧变量可以从我们添加的每个方法访问,并且从主对象对它的引用被覆盖,因此它只能从该范围中的“old”访问。你基本上有一堆“旧”变量通过它们级联来找到一个处理给定参数数量的变量。

function addMethod(object, name, fn) {

  // Get a reference to the existing method name.
  var old = object[name];

  /*
    Write over the method name with a new method
    that checks for a specific argument length.
    If the method is called with a different
    argument length and "old" exists call the
    old method.
  */

  object[name] = function(){

       if (fn.length == arguments.length)

          return fn.apply(this, arguments)

       else if (typeof old == 'function')

          return old.apply(this, arguments);

    };
}

答案 3 :(得分:1)

function.length是函数期望的参数个数,形式参数。 arguments.length是传递给函数的实际参数数量,这意味着已经存在的函数,因为没有参数可以传递给新函数;它没有被调用,只是一个价值。 arguments对象是一个类似于数组的对象,包含传递的参数。 (知道这个长度属性可能更有意义)。给定参数和形式参数的长度之间的差异在于函数可以传递多于或少于参数的参数。例如,console.log()接受任意数量的参数。其中一些函数只是在arguments对象周围包装一个循环,并对arguments对象中的每个元素执行某些操作。添加的每个方法都存储为作为addMethod函数的第一个参数给出的对象的属性。变量old只是一个变量,名称不是理解概念所必需的。也许作者暗示old对象及其属性的可变性(可变性是赋予属性一个新值/在声明后修改对象的能力)。

答案 4 :(得分:0)

你的第一个问题大部分是由dandavis解决的。评论。 arguments.length给出了应用于行

中声明的函数的参数数量
object[name] = function(){

该方法已存储'在第一行代码中声明的ninja对象中,并传递给后续调用。