函数表达式

函数 表达式 是在需要表达式的地方创建的函数。您经常会遇到赋值给变量的函数表达式。虽然函数声明始终需要名称,但您可以使用函数表达式通过省略标识符并在 function 关键字后跟一对包含可选参数的圆括号来创建匿名函数

const myVariable = function() { };

然后,您可以使用变量的标识符调用这些函数表达式

const myVariable = function() {
    console.log( "This is my function." );
};

myVariable();
> "This is my function."

您还可以使用函数表达式来创建命名函数,语法类似于函数声明

const myVariable = function myFunction() {
    console.log( "This is my function." );
};

myVariable();
> "This is my function."

但是,与函数声明不同,命名的函数表达式只能在函数本身内通过函数名访问

const myVariable = function myFunction() {
  console.log( `I'm a ${ typeof myFunction }.`);
};

typeof myFunction;
> "undefined"

typeof myVariable;
> "function"

myVariable();
> "I'm a function."

与函数表达式关联的名称主要用于调试。命名的函数表达式也可以递归地调用自身,尽管这在现代开发中不是一个非常常见的用例

const myVariable = function myFunction() {
    console.log( "One second elapsed." );
    setTimeout( myFunction, 1000 );
};

setTimeout( myVariable, 1000 );
> "One second elapsed."
> "One second elapsed."
> "One second elapsed."

箭头函数表达式

箭头函数表达式(通常称为“箭头函数”,或者极少情况下称为“lambda 函数”)是在 ES6 中引入的,旨在为创建匿名函数表达式提供简洁的语法,并具有一些独特的行为。

您可以在需要表达式的任何地方创建箭头函数,例如,作为赋值给变量的值。在其最常见的形式中,箭头函数由一对匹配的圆括号(包含零个或多个参数)、一个由单个等号和大于号字符组成的箭头 (=>) 以及一对匹配的包含函数体的花括号组成

const myFunction = () => {};

在某些条件下,您可以使语法更加紧凑。如果您只使用一个参数,则可以省略起始圆括号

const myFunction = myParameter => {};

当您希望函数体返回单个表达式的值时,既不需要将函数体括在花括号中,也不需要return 关键字

const myFunction = () => 2 + 2

myFunction()
> 4

箭头函数的独特之处在于它们没有自己的 argumentsthis 值的上下文。相反,它们从箭头函数的 词法封闭环境(即最近的提供这些上下文的封闭函数)继承这两个值。

function myParentFunction() {
    this.myProperty = true;
    let myFunction = () => {
            console.log( this );
    }
    myFunction();
};

let myInstance = new myParentFunction();
> Object { myProperty: true }

调用箭头函数

箭头函数绑定参数的方式与其他类型的函数不同。箭头函数体中的 arguments 对象从该箭头函数最近的词法封闭环境继承其值

function myFunction() {
    let myArrowFunction = () => {
            console.log( arguments[ 0 ] );
    }
    myArrowFunction( true );
};

myFunction( false );
> false

在此示例中,使用参数 false 调用的外部函数使用参数 true 调用内部箭头函数。由于箭头函数内部的 arguments 对象解析为外部函数中的绑定,因此内部函数记录外部函数的 false

如果没有 arguments 对象可以从父上下文继承,则未定义箭头函数的 arguments 对象,并且尝试访问它会导致错误

let myArrowFunction = () => {
    console.log(arguments);
};
myArrowFunction( true );
> Uncaught ReferenceError: arguments is not defined

立即调用函数表达式 (IIFE)

立即调用函数表达式 (IIFE),有时也称为“自执行匿名函数”,是一种在定义时立即调用的函数表达式。IIFE 使用通过将函数括在分组运算符中创建的函数表达式。然后,第二对匹配的圆括号调用该函数,可以紧跟在函数定义本身之后,也可以紧跟在分组运算符之后。如果您使用标准函数,则这两种方法之间没有实际区别

(function() {
    console.log( "IIFE.")
    }
)();
> "IIFE."

(function() {
    console.log( "IIFE.")
    }
());
> "IIFE."

第一个示例调用分组的函数表达式。第二个示例调用分组运算符内部的函数声明,然后将最终结果评估为分组表达式。在这两种情况下,结果都是相同的。

但是,当您的 IIFE 是箭头函数时,则存在差异。在这种情况下,用于调用函数的圆括号必须位于分组运算符之外,因为箭头函数本身不是表达式,但必须在需要表达式的上下文中创建。尝试从分组运算符的范围内调用箭头函数意味着调用尚未在表达式上下文中创建的箭头函数

( () => {
    console.log( "IIFE." );
}() );
> Uncaught SyntaxError: missing ) in parenthetical

由于分组运算符需要表达式,因此定义了其中的箭头函数,从而使跟随它们的圆括号可以调用分组表达式

( () => {
    console.log( "IIFE." );
} )();
> "IIFE."

传统应用程序经常使用 IIFE 来管理作用域,特别是为了避免使用函数作用域变量函数声明污染全局作用域。在 ES6 中引入块级作用域之前,常见的做法是将整个脚本包装在 IIFE 中,以防止意外污染全局作用域。

检查您的理解程度

您可以在函数外部通过名称调用命名的函数表达式吗?