函数是根基

目录

在 JavaScript 中,函数是第一型对象,也就是说函数可以共处,可以将其视为其他任意类型的 JavaScript 对象。就像普通的 JavaScript 数据类型,函数可以被任意变量进行引用,或声明称对象字面量,甚至可以将其作为函数参数进行传递。

函数是第一型对象

对象在 JavaScript 中有如下功能:

  • 可以通过字面量进行创建
  • 可以赋值给变量、数组或其他对象的属性
  • 可以作为参数传递给函数
  • 可以作为函数的返回值进行返回
  • 可以拥有动态创建并赋值的属性

在 JavaScript 中,函数拥有全部这些功能,也就是说可以像这门语言的其他对象一样使用。因此,我们说函数是第一型对象。 除了可以像其他对象类型一样使用外,函数还有一个特殊的功能,他们可以被调用。 这些调用通常以异步方式进行调用。

函数声明

JavaScript 函数是使用函数字面量进行声明从而创建函数值的,就像使用数字字面量创建数字值一样。记住,作为第一型对象,函数可以像其他值一样在语言中使用,比如字符串和数字。

函数字面量由四个部分组成:

  1. function 关键字
  2. 可选名称,如果指定名称,则必须是一个有效的 JavaScript 标志符
  3. 符号内部,一个以逗号分隔的参数列表。各个参数名称必须是有效的标志符,而且参数列表允许为空。即使是空参数列表,圆括号也必须始终存在
  4. 函数体,包含一个大括号内的一系列 JavaScript 语句。函数体可以为空,但大括号必须始终存在

作用域和函数

当我们声明一个函数时,我们不仅要关注该函数可用的作用域,还要关注该函数自身所创建的作用域,以及函数内部的声明是如何影响这些作用域。在 JavaScript 中,作用域是由 function 进行声明的,而不是代码块。声明的作用域创建于代码块,但不是终结于代码块。

if (window) {
  var x = 123;
}
alert(x); // 弹出 123

函数调用

调用方式:

  1. 作为一个函数进行调用
  2. 作为一个方法进行调用,在对象上进行调用,支持面向对象编程
  3. 作为构造器进行调用,创建一个新对象
  4. 通过 apply() 或 call() 方法进行调用
expression(arg1, arg2);

从参数到函数形参

当一个参数列表作为函数调用的一部分时,这些参数会按照函数声明里的形参声明顺序,将参数值分别赋值给这些形参

如果传入的参数个数和声明的形参数量不一致,不会抛错,JavaScript 处理策略:

  • 如果实际传递的参数数量大于函数声明的形参数量,超出的参数则不会配给形参名称;但依旧有办法获取它们
  • 如果实际传递的参数数量小于于函数声明的形参数量,则没有对应参数的形参会赋值为 undefined

注意: 所有函数调用会传递两个隐式参数:arguments 和 this

  1. arguments 参数:传递给函数的所有参数的一个集合,该集合有一个 length 属性,其值是全部参数的个数,单个参数值可以像访问数组索引一样进行获取,但不能使用数组的各种方法。只需将 arguments 想象成一个类数组结构,并且只拥有数组的某些特性。
  2. this 参数:一个函数被调用时,除了传入了函数的显式参数以外,名为 this 的隐式参数也被传入了函数。this 参数引用了与该函数调用进行隐式关联的一个对象,被称之为函数上下文

作为函数调用

function func() {}
func();

var func1 = function () {};
func1();

作为方法进行调用

当一个函数被赋值给对象的一个属性,并并使用引用该函数的这个属性进行调用时,那么函数就作为该对象的一个方法进行调用的

var obj = {};
obj.func = function () {};
obj.func();

作为构造器进行调用

将函数作为构造器进行调用,我们要在函数调用前使用 new 关键字

function func() {
  return this;
}
const newFunc = new func();

构造器的超能力

将函数作为构造器进行调用,是 JavaScript 的一个超特性,因为构造器调用时,如下特殊行为会发生:

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

构造器的目的是要创建一个新对象并对其进行设置,然后将其作为构造器的返回值进行返回。任何干扰这种意图的函数都不适合作为构造器

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

到目前为止,函数调用方式之间的主要差异是:作为 this 参数传递给执行函数的上下文对象之间的区别

函数调用方式上下文对象
作为方法调用方法拥有者
作为全局函数调用window
作为构造器调用新创建的对象示例

使用 apply() 和 call() 方法

apply() 传入两个参数:

  1. 作为函数上下文的对象
  2. 作为函数参数所组成的数组
func.apply(obj, [1, 2, 3])

call() 传入两个参数:

  1. 作为函数上下文的对象
  2. 函数传入的参数是一个参数列表
func.call(obj, 1, 2, 3)

总结

  • 以函数式语言学习 JavaScript,从而编写更复杂的代码
  • 函数是第一型对象,就像是 JavaScript 中的其他任意对象一样。和其他任意对象类型一样,他们有如下特点:
    • 通过字面量进行创建
    • 赋值给变量或属性
    • 作为参数进行传递
    • 作为函数结果进行返回
    • 拥有属性和方法
  • 每个对象都有一个与众不同的”小宇宙“,函数的小宇宙是他可以被调用
  • 函数是通过字面量进行创建的,其名称是可选的
  • 在页面生命周期内,浏览器可以将函数作为各种类型的事件处理程序进行调用
  • 函数内部的作用域与大多数其他语言的作用域不同。(具体来说:变量的作用域开始于声明处,结束于函数尾部,其会跨域边界块)。内部函数在当前函数的任何地方都可以(提升),即便是提前引用
  • 函数形参列表和实际参数列表的长度可以不同(未赋值的参数被设置为 undefined;多出的参数是不会绑定到参数名称的)
  • 每个函数调用都会传入两个隐式参数(arguments:实际传入的参数集合;this:作为函数上下文的对象引用)
  • 可以用不同的方法进行函数调用,不同调用机制决定了函数上下文的不同
    • 作为普通函数调用时,其上下文是全局对象(window)
    • 作为方法进行调用时,其上下文是拥有该方法的对象
    • 作为构造器进行调用时,其上下文是一个新分配的对象
    • 通过函数的 apply() 和 call() 方法进行调用时,上下文可以设为任意值