函数

函数是一个模块化、可重用的语句块,用于执行一组相关任务,例如根据提供给函数的参数计算和返回值。与所有非原始值一样,函数也是对象。它们是独特的对象,因为可以调用它们来执行代码,以参数的形式向其传递数据,并返回一个值。

函数被认为是“一等”对象,这意味着尽管它们具有独特的行为,但它们可以在与任何其他 JavaScript 对象完全相同的上下文中使用。例如,可以将函数分配给变量、作为参数传递给其他函数,以及由其他函数返回。

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

定义为对象的属性的函数通常称为“方法”。与使用 var 声明的变量一样,在封闭函数外部进行的函数声明会作为方法添加到全局对象

函数声明

函数声明(也称为“函数语句”或“函数定义”)创建一个命名的函数,该函数可以在其包含范围内的其他位置调用。函数声明由 function 关键字、后跟标识符、用括号括起来的逗号分隔的参数列表以及称为“函数体”的块语句组成。您经常会遇到不以分号结尾的函数声明;由于函数声明是一个语句,因此尾随分号可以由 ASI 推断出来。

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

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

作为 JavaScript 早期设计决策的遗留,函数声明与使用 var 声明的变量一样,也受到相同的遗留提升行为的约束,这意味着函数声明会被提升到其作用域的顶部,因此可以在声明之前调用,无论该作用域是否受严格模式约束

"use strict";
{
    myFunction();
    function myFunction() {
        console.log( "This is my function." );
    };
}
> "This is my function."

严格模式之外,函数声明使用 JavaScript 的遗留作用域行为,这意味着函数声明的作用域限定在其最近的封闭函数

function myFunction() {
    function myNestedFunction() {
        console.log( "This is my nested function." );
    }
    myNestedFunction();
};

myFunction();
> "This is my nested function."

myNestedFunction();
>Uncaught ReferenceError: myNestedFunction is not defined

严格模式下,函数声明的作用域限定在其最近的封闭块,这与使用 letconst 声明的变量相同

"use strict";
{
    function myFunction() {
        console.log( "This is my function." );
    };
}

myFunction();
> Uncaught ReferenceError: myFunction is not defined

函数调用

与变量一样,声明函数时使用的标识符充当值的符号名称。仅通过标识符引用函数只会返回函数对象,而不会执行其包含的函数

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

myFunction;
> myFunction() {
   console.log( "This is my function." );
}

要执行函数体内的代码,请通过在函数名称后跟一对匹配的括号来调用(或调用)该函数

function myFunction() {
    console.log( "My function has been executed." );
}

myFunction();
> "My function has been executed."

函数定义中的参数充当值的占位符变量,这些值可以在调用函数时传递到函数体中。调用函数时括号中的值是“实参”(尽管您可能会在某些文档中看到“实参”用于描述实参和形参)

function myFunction( myParameter ) {
   console.log( `The value is: ${ myParameter }.` );
};

myFunction( "this string" );
> "The value is: this string."

如果省略了预期的实参,则生成的形参将包含 undefined 值,因为该形参已声明到函数体,但未初始化值

function myFunction( myParameter ) {
   console.log( `The value is: ${ myParameter }.` );
};

myFunction();
> "The value is: undefined."

您可以通过以与初始化变量相同的方式设置默认形参值:赋值运算符 (=) 后跟一个值。如果您稍后为该函数指定一个实参,则新值将覆盖默认值

function myFunction( myParameter = "omitted" ) {
   console.log( `The value is: ${ myParameter }.` );
};

myFunction( "this string" );
> "The value is: this string."

myFunction();
> "The value is: omitted."

非箭头函数的函数体还可以访问从零开始的、类似数组arguments 对象,其中包含作为实参传递的任何值,无论函数定义是否指定了形参

function myFunction() {
   console.log( arguments );
};

myFunction( 3, true, "My string" );
> Arguments { 0: 3, 1: true, 2: "My string",  }

可变参数函数

arguments 对象允许您创建基本可变参数函数,这些函数可以接受可变数量的实参

function myFunction() {
    let result = "";
    for (let i = 0; i < arguments.length; i++) {
        result += arguments[i] + " - ";
    }
    console.log( result );
};

myFunction( "My first string", "My second string", "my third string" );\
> "My first string - My second string - my third string - "

但是,这种可变参数函数方法在现代 JavaScript 开发中很少使用。更常见的是使用更现代且更易读的剩余参数语法,它创建一个命名形参,该形参初始化为包含超出显式指定的任何实参的数组

function myFunction( mySeparator, ...myStrings ) {
  console.log( myStrings.join( mySeparator ) );
};

myFunction( " - ", "My first string", "My second string", "my third string" );
> "My first string - My second string - my third string"

parameter 绑定不同,剩余参数语法可以按预期与箭头函数形参一起使用

function myOuterFunction() {
    let myInnerArrowFunction = ( ...myParams ) => {
        console.log( myParams[ 0 ] );
    }
    myInnerArrowFunction( true );
};

myOuterFunction( false );
> true

let myArrowFunction = ( ...myParams ) => {
    console.log( myParams[ 0 ] );
};

myArrowFunction( true );
> true`
``