函数的四种调用方式

一共有四个不同的方式可以进行函数调用,每种方式都有自己的细微差别。

  • 作为一个函数进行调用,是最简单的形式
  • 作为一个方法在对象上进行调用,支持面向对象编程
  • 作为构造器进行调用,创建一个新对象
  • 通过apply()call()方法进行调用

除了最后一种方式,其他方法的函数的调用,都是通过在表达式后面加上小括号来执行函数引用。如果给函数传递参数,则需要将参数放在小括号里并以逗号分隔:expression(arg1,arg2)

实参( arguments )与形参( parameters )

如果实参与形参的数量不一致,处理策略如下:

  • 如果实参数量大于形参数量,超出的实参不会赋值给形参
  • 如果实参数量小于形参数量,则未被赋值的形参默认为undefined

注意:所有函数调用都会传递两个隐式参数:argumentsthis
所谓隐式,意味着这些参数不会显示列在函数签名中,但是他们默认地传递给函数并存在于函数作用域。在函数内部,它们可以像其它显式命名的参数一样使用。

arguments参数

arguments参数是传递给函数的所有参数的一个集合。该集合有一个length属性,其值是全部参数的个数,单个参数值可以像访问数组索引一样进行获取,例如arguments[0]代表获取的第一个参数。

但是请注意,虽然可以argument有一个length属性,但是它并不是一个数组,而是一个类数组。不过,我们还是可以使用call()apply()让它使用数组的一些自有方法。

this参数

this参数引用了该函数调用进行隐式关联的一个对象被称之为函数上下文( function context )。

this依赖于函数的调用方式,也可称其为调用上下文( invocation cntext )。其实开篇所讲的四种函数调用方式的主要区别在于如何定义调用时的this

作为函数进行调用

如果一个函数不是作为方法,构造器,或者通过apply()call()进行调用的,我们则认为它是“作为函数”调用的。

以这种方式调用时,函数的上下文是全局对象:window对象。所以“函数作为函数”进行调用是“作为方法”进行调用的一种特殊情况。但由于函数的所有者是 window,通常被认为是自己的机制。

作为方法进行调用

当一个函数被赋值给对象的一个属性,并使用引用该函数的这个属性进行调用时,那么函数就是作为该对象的一个方法进行调用的。
将函数作为对象的一个方法(method)进行调用时,该对象就变成了函数上下文,并且在函数内部可以用this来访问。这是可以将 JavaScript 作为面向对象代码进行编写的主要手段之一。

与“作为函数”进行调用对比:“作为函数”进行调用所指的函数是定义在 Window 对象上的,而且调用的时候不必显式指定Window的引用,除此以外,这两种方式是一样的。

在下面的代码中,对比“作为函数”和“作为方法”调用的差异与相似之处:

运行结果:
function-method

在上述代码中,我们期望函数上下文是该方法所在的对象。这个功能对于以面向对象方式编写 JavaScript 至关重要。这意味着,我们可以在任意方法中,通过this引用该方法所属的对象——面向对象编程的基本概念。

即使上述所有这些例子中调用的都是相同的函数,其函数上下文也会随着函数调用方式的变化而变化,而不是取决于函数是怎样声明的。这意味着我们不需要创建单独的函数副本,就可以在不同的对象上完成完全相同的处理过程——面向对象编程的宗旨。

作为构造器进行调用

将函数作为构造器( constructor )进行调用,我们要发函数调用前使用new关键字,将函数作为构造器进行调用,是 JavaScript 的一个超级特性,因为构造器调用时,如下特殊行为就会发生:

  • 创建一个新的空对象
  • 新创建的对象作为this参数传递给构造函数,从而成为构造函数的上下文
  • 如果没有显式的返回值,新创建的对象则作为构造器的返回值进行返回

通过构造器,使用相同的模式就可以更容易地创建多个对象,而无需再一遍又一遍地重复相同的代码。通过代码,作为构造器的构造体,只需编写一次。

使用 apply() 和 call() 方法进行调用

到目前为止,我们看到,函数调用方式之间的主要差异是:作为this参数传递给执行函数上下文对象之间的区别。作为全局函数调用,上下文对象永远是window;作为方法进行调用,上下文对象是方法的拥有者;作为构造器调用,上下文对象则是新创建的对象实例。

但是,如果我们想自由指定函数上下文呢?比如显式指定一个上下文?

使用 apply()与 call() 方法

在函数调用的时候,JavaScript 为我们提供了一种方式,可以显式指定任何一个对象作为其函数上下文。JavaScript 的每个函数都有apply()call()方法,使用其中任何一个都可以实现上述功能。

通过函数的apply()方法来调用函数,我们要给apply()传入两个参数:一个是作为函数下文的对象,另外一个是作为函数参数所组成的数组,call()的使用方法类似,唯一不同的是,给函数传入的参数是一个参数列表,而不是单个数组。

如下代码展示了这两种方法的实际应用:

运行结果:
call-appy

在回调中强制指定函数上下文

让我们看一个强制选择函数上下文对象的具体例子,用一个遍历数组元素并执行操作的简单函数为例。

在命令式编程中,通常是将数组传递给一个方法,并使用for循环语句遍历每一个元素,然后再对每个元素执行操作

相比之下,函数式编程则是创建一个函数来执行单个元素,并将每个元素都传递给该函数

两者之间的区别在于思维层面:函数比命令式语句更适合作为程序的构建模块。

为了使函数式编程更容,很多流利的 JavaScript 库都提供一个“for-each”的函数,比如 JQuery 库的$.each(),该函数在数组的每个元素上都进行回调调用。在函数式编程中,这种风格更简洁,并优于传统的那些for语句。
接下来的代码中,构建我们自己版本的each函数

运行结果:
functional-each

我们使用 callback 回调函数的call()方法,将当前元素作为第一个参数传入并且将元素索引作为第二个参数传入。这就会让当前元素变成函数上下文,而索引则作为单独一个参数传递给 callback 回调。

了解函数在 JavaScript 中的各种调用方式,有助于我们以函数式语言学习 JavaScript,从而编写更复杂的代码,因为 JavaScript 是一门函数式语言( funcational language )。

发表评论

电子邮件地址不会被公开。 必填项已用*标注