1.4.1 定义函数

函数声明+函数表达式(普通函数+生成器函数)、箭头函数、函数构造器

定义函数的几种方式:

  1. 普通函数:函数声明、函数表达式

  2. 生成器函数:生成器函数声明、生成器函数表达式

  3. 箭头函数

  4. 构造函数:用 new 操作符

    • 不推荐使用这种方式来创建函数。因为它们都需要函数体作为字符串,这可能会妨碍 JavaScript 引擎的一些优化,还可能会导致其它问题。

    • new Function (arg1, arg2, ... argN, functionBody) // 普通函数对象
      new GeneratorFunction ( arg1, arg2, ... argN, functionBody) // 生成器函数对象
    • 说明:GeneratorFunction 不是全局对象,但可以从生成器函数实例中获取

接下来,详细介绍下普通函数的函数声明和函数表达式。

1. 函数声明

函数声明,也称函数语句。

  • function declaration

  • function statement

function say(xx){
    //...
}

2. 函数表达式

函数表达式,可以是匿名的,也可以有自己的名字。

  • an anonymous function expression,匿名函数表达式

  • a named function expression,命名函数表达式

    • 一个好处:更方便调试,因为堆栈跟踪会包含函数的名字

// 匿名函数表达式
const square = function(number) {
  return number * number;
};
const x = square(4);

// 命名函数表达式
const factorial = function fac(n) {
  return n < 2 ? 1 : n * fac(n - 1);
};
console.log(factorial(3));

以上两个例子都不是以 function 关键字开头的。涉及到函数的语句,不以 function 关键字开头的,就是函数表达式。

当函数只使用一次时,一个常见的模式就是 IIFE,Immediately Invoked Function Expression,立即调用函数表达式。IIFE 也是一种函数表达式,它在声明的同时被调用:

// IIFE,立即调用函数表达式
(function () {
  // statements
})();

注意:函数表达式不会被提升到作用域的开头,所以它在定义之前是不能被调用的。

3. 注意事项

3.1 声明 vs 表达式 vs 构造器

Declaration vs. Expression vs. Constructor

它们做的事情大致相同,但有一些细微的差别:

  1. 函数名称 vs 函数变量

    • 函数名称不能修改,而函数变量可以被重新赋值

    • 函数表达式中的函数名,只能在函数体中使用

    • 当函数通过 toString() 方法进行序列化时,会包含函数名

  2. 是否发生函数提升

    • 用函数声明定义的函数,可以在函数声明本身之前使用(会提升)

    • 用函数表达式或构造函数定义的函数,不会发生函数提升(不会提升)

  3. 是否继承当前作用域

    • 用函数声明和函数表达式定义的函数,会继承当前作用域(函数会形成一个闭包)

    • 用 Function 构造函数定义的函数,不会继承任何作用域,除了全局作用域(所有的函数都会继承)

  4. 是否只解析一次

    • 用函数声明和函数表达式定义的函数,只解析一次。虽然函数表达式每次都会创建一个闭包,但函数体不会重新解析

    • 用 Function 构造函数定义,每次调用都会解析一次(所以不建议使用)

    • 注:嵌套在 Function() 构造函数的字符串参数中的函数表达式和函数声明,不会重复解析

const y = function x() {};
console.log(x); // ReferenceError: x is not defined

3.2 函数声明“变”表达式

函数声明很容易变成函数表达式(通常是无意的)。比如:

  • 当函数声明成了表达式的一部分

  • 当函数声明不再是函数或脚本的“源元素”(source element),源元素是脚本或函数体中的非嵌套语句

let x = 0;               // source element
if (x === 0) {           // source element
  x = 10;                // not a source element,在 if 语句里
  function boo() {}      // not a source element
}

function foo() {         // source element
  let y = 2;             // source element
  function bar() {}      // source element
  while (y < 10) {       // source element
    function blah() {}   // not a source element,在 while 循环里
    y++;                 // not a source element
  }
}

3.x 其它

  • eval(str)

在 JavaScript 中,还可以根据条件定义函数。如下:

let op;:
if (flag) {
  op = function() {};
}

4. 扩展阅读

Last updated