ZetCode

JavaScript 函数

最后修改于 2023 年 10 月 18 日

在本文中,我们将展示如何在 JavaScript 中使用函数。

一个 函数 是将零个或多个输入参数映射到零个或多个输出参数。

使用函数,我们可以减少代码重复并提高其清晰度。通过使用函数,可以将更复杂的任务分解为更简单的步骤。

函数可以分配给变量,作为参数传递给函数或从其他函数返回。

JS 函数定义

JS 函数使用 function 关键字定义。关键字后面跟着函数名,用括号括起来并用逗号分隔的函数参数列表,以及用大括号 {} 括起来的函数体。

函数体由 JS 语句组成。函数使用 return 关键字返回值。

main.js
function add(x, y) {

    return x + y;
}

let z = add(10, 5);
console.log(z);

该示例定义了一个简单的 add 函数。

function add(x, y) {

    return x + y;
}

这是函数的定义。它返回一个简单加法表达式的值。它有两个参数,用逗号分隔。使用 return 关键字,我们将计算值传递给调用者。

let z = add(10, 5);

该函数被调用。它接收到两个整数参数。返回值存储在 z 变量中。

JS 函数表达式

function 关键字可用于在表达式中定义函数。函数表达式允许我们创建匿名函数。

函数表达式在其他编程语言中称为 lambda 表达式。

main.js
let z = function add(x, y) {

    return x + y;
}

console.log(z(10, 10));

我们使用函数表达式重写了前面的示例。

let z = function add(x, y) {

    return x + y;
}

我们将函数表达式传递给 z 变量。

console.log(z(10, 10));

该函数通过 z 变量调用。

JS 匿名函数

匿名函数没有名称。在许多情况下,定义命名函数是多余的。

main.js
setTimeout(
    function() {
        console.log('Hello there!')
    }, 3000
);

setTimeout 函数定义了一个处理程序函数,该函数在指定的时间(毫秒)后执行。不需要定义命名函数;匿名函数在这里非常合适。

数组集合包含多个在其元素上工作的函数。

main.js
let vals = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

let res = vals.reduce(function (x, y) { return x + y; });
console.log(res);

reduce 函数用于计算 JS 数组元素的总和。该函数将一个 reducer 函数作为参数。在数组的所有元素上运行 reducer 的结果是总和值。

let res = vals.reduce(function (x, y) { return x + y; });

reducer 函数是一个匿名的 JS 函数。

JS 函数默认值

在 JavaScript 中,函数的参数默认为 undefined。我们可以为函数参数提供自定义默认值;如果未为参数提供任何值,则使用它们。

main.js
function power(a, b = 2) {

    if (b == 2) {

        return a * a
    }

    let value = 1

    for (let i = 0; i < b; i++) {

        value *= a
    }

    return value;
}


let r1 = power(3)
console.log(r1);

let r2 = power(3, 3)
console.log(r2);

let r3 = power(3, 5)
console.log(r3);

我们创建了一个 power 函数。该函数有一个带隐式值的参数。我们可以使用一个或两个参数调用该函数。

$ node main.js 
9
27
243

JS 可变参数函数

可变参数函数可以接受可变数量的参数。例如,当我们想要计算值的总和时,我们可能需要传递四个、五个、六个等值给函数。

我们使用 ... (省略号) 运算符在 JavaScript 中定义一个可变参数函数。

main.js
function sum(...vals) {

    return vals.reduce(function (x, y) { return x + y; });
}

console.log(sum(1, 2));
console.log(sum(1, 2, 3));
console.log(sum(1, 2, 3, 4));

该程序定义了一个 sum 函数,它可以接受可变数量的参数。

function sum(...vals) {

    return vals.reduce(function (x, y) { return x + y; });
}

vals 是一个 JS 数组。我们使用它的 reduce 函数来计算值的总和。 reduce 函数接受

$ node main.js 
3
6
10

JS 箭头函数

箭头函数是一个语法更短的匿名函数。它用一对包含参数列表的括号定义,后跟一个胖箭头 (=>) 和一对用于分隔主体语句的大括号 {}

如果箭头函数只有一个参数,则可以省略括号。如果它包含单个语句,也可以省略大括号。

main.js
let vals = [-1, 2, 3, -4, 5, 6, -7, 8, 9, 10];

let res = vals.filter(e => e > 0);
console.log(res);

在该示例中,我们过滤一个整数数组。filter 函数将一个谓词函数作为参数;它定义了要过滤哪些值。谓词用箭头函数定义。

JS 嵌套函数

嵌套函数,也称为内部函数,是在另一个函数内部定义的函数。

main.js
let user = { fname: 'John', lname: 'Doe', occupation: 'gardener' }

function sayHello(u) {

    let msg = buildMessage(u);

    console.log(msg);

    function buildMessage(u) {
        return `${u.fname} ${u.lname} is a ${u.occupation}`;
    }
}

sayHello(user);

在该示例中,我们有一个嵌套的 buildMessage 函数,它是在 sayHello 函数内部定义的辅助函数。

JS 闭包

一个 闭包 是一个匿名的、嵌套的函数,它保留对在闭包体外定义的变量的绑定。闭包可以拥有自己独特的状态。当我们创建函数的新实例时,状态就会变得孤立。

main.js
function intSeq() {

    let i = 0;

    return function () {

        return i++;
    };
}

let nextInt = intSeq();

console.log(nextInt());
console.log(nextInt());
console.log(nextInt());
console.log(nextInt());

let nextInt2 = intSeq();

console.log(nextInt2());
console.log(nextInt2());

我们有 intSeq 函数,它生成一个整数序列。它返回一个递增 i 变量的闭包。

function intSeq() {

    let i = 0;

    return function () {

        return i++;
    };
}

在函数中定义的变量具有局部函数作用域。但是,在这种情况下,即使在 `intSeq` 函数返回后,闭包仍绑定到 `i` 变量。

let nextInt = intSeq();

我们调用 intSeq 函数。它返回一个函数,该函数将递增一个计数器。返回的函数关闭了变量 i 以形成一个闭包。闭包绑定到 nextInt 名称。

let nextInt2 = intSeq();

console.log(nextInt2());
console.log(nextInt2());

下一次调用 `intSeq` 函数会返回一个新的闭包。这个新的闭包有自己独立的状态。

$ node main.js 
0
1
2
3
0
1

JS 高阶函数

高阶函数通过将其他函数作为参数或返回它们来对其他函数进行操作。

main.js
let vals = [-3, 0, 1, 2, 5, 4, 9, 11, 8, 7];

let isEven = e => e % 2 == 0;
let isOdd = e => e % 2 != 0;

function doFilter(vals, pred) {

    let filtered = [];

    for (let i = 0; i < vals.length; i++) {
        pred(vals[i]) ? filtered.push(vals[i]) : null;
    }

    return filtered;
}

let res = doFilter(vals, isEven);
console.log(res);

res = doFilter(vals, isOdd);
console.log(res);

doFilter 是一个高阶函数。

function doFilter(vals, pred) {

    let filtered = [];

    for (let i = 0; i < vals.length; i++) {
        pred(vals[i]) ? filtered.push(vals[i]) : null;
    }

    return filtered;
}

该函数将一个谓词函数(一个产生布尔值的函数)作为参数。它过滤掉所有满足给定谓词的值。

$ node main.js 
[ 0, 2, 4, 8 ]
[ -3, 1, 5, 9, 11, 7 ]

JS 成员函数

成员函数是在类定义内定义的函数。成员函数在某些语言中被称为方法,包括 Java 和 C#。

main.js
class Person {

    constructor(firstName, lastName, email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }

    info() {
        return `${this.firstName} ${this.lastName}, ${this.email}`;
    }
}

let person = new Person('John', 'Doe', 'jdoe@example.com');
console.log(person.info());

一个类用 class 关键字定义。

info() {
    return `${this.firstName} ${this.lastName}, ${this.email}`;
}

info 是在类中定义的成员函数。

JS 函数构造函数

可以使用函数构造函数创建 JS 对象。它将值作为参数。属性使用 this 关键字设置。成员函数使用 this 和 function 关键字创建。新对象使用 new 关键字创建。

main.js
function Person(firstName, lastName, email) {

    this.firstName = firstName;
    this.lastName = lastName;
    this.email = email;

    this.info = function () {
        return `${this.firstName} ${this.lastName}, ${this.email}`;
    }
}

let person = new Person('John', 'Doe', 'jdoe@example.com');
console.log(person.info());

在程序中,我们定义一个 Person 类型。

function Person(firstName, lastName, email) {

Person 的构造函数使用 function 关键字定义。

this.info = function () {
    return `${this.firstName} ${this.lastName}, ${this.email}`;
}

在构造函数定义中,我们定义了 info 成员函数。

$ node main.js 
John Doe, jdoe@example.com

JS 函数类型

JavaScript 也是一种强大的面向对象语言。每个函数也是一个对象。它的类型是 Function。函数可以使用 new Function 构造函数创建,尽管这不是一个推荐的做法。

main.js
let x = 3;
let y = 8;

let square = new Function('x', 'return x * x');

let res = square(x);
console.log(res);

res = square(y);
console.log(res);

在该示例中,我们使用 Function 构造函数定义一个 square 函数。

JS 函数提升

提升是将函数、变量或类的声明移到其作用域的顶部,在代码执行之前。

main.js
console.log(sum(1, 2, 3, 4, 5));

function sum(...vals) {

    return vals.reduce((x, y) => x + y);
}

我们在定义 sum 函数之前调用它。


但是,提升不适用于函数表达式。

main.js
console.log(sum(1, 2, 3, 4, 5));

let sum = (...vals) => vals.reduce((x, y) => x + y);

程序以错误结束:ReferenceError: 在初始化之前无法访问 'sum'

JS 生成器

生成器是可以退出并稍后重新进入的函数。它们的环境(变量绑定)在函数调用之间保存。生成器函数使用 function* 语法创建。

调用生成器函数会返回一个迭代器。当调用迭代器的 next 方法时,生成器函数的体被执行,直到第一个 yield 表达式;它返回一个具有 value 属性的对象,该属性包含生成的 value 值。 done 属性指示生成器是否已生成其最后一个值。

main.js
function* idxGen(max) {
    let idx = 0;

    while (idx < max) {

        yield idx++;
    }
}

const g = idxGen(100);

for (let i of g) {

    console.log(i);
}

该示例创建一个生成器,该生成器生成最多到指定最大值的索引。

function* idxGen(max) {
    let idx = 0;

    while (idx < max) {

        yield idx++;
    }
}

我们定义了 idxGen 生成器函数。yield 关键字退出生成器并返回值。下一次调用迭代器的 next 函数时,我们继续执行 yield 关键字之后的行。局部变量 idx 被保留。当 idx 达到 max 值时,生成器生成其最后一个值,并将 done 属性设置为 true。

const g = idxGen(100);

我们调用 idxGen;它返回一个迭代器。

for (let i of g) {

    console.log(i);
}

在一个 for 循环中,我们迭代生成的值。

JS IIFE

立即调用的函数表达式 (IIFE) 是一个定义为表达式的函数,并在创建后立即执行。它使用函数作用域生成词法作用域。在引入 JS 模块之前,这是一种广泛使用的方法。

main.js
let sum = (function (a, b) {
    return a + b;
})(15, 50);

console.log(sum);

我们定义了一个加法函数,该函数立即使用两个参数执行。结果存储在 sum 变量中。

$ node main.js 
65

来源

JS 函数

在本文中,我们介绍了 JavaScript 函数。

作者

我的名字是 Jan Bodnar,我是一位热情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我撰写了 1,400 多篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

查看 所有 JavaScript 教程。