JavaScript函数

前面的文章中已经大量地使用了JavaScript内置的函数和对象类型,本文将介绍如何创建自己的函数,并讨论函数类型、匿名函数、函数返回可迭代数据等内容。

创建函数

JavaScript中,定义函数一般会使用function关键字,基本格式如下:

JavaScript
function <函数名>(<参数列表>) {
    <函数代码>
}

其中,<函数名>是函数调用的名称,一般采用首字母小写,然后每个单词首字母大写的形式。<参数列表>用于向函数内部传递数据,可以有一个或多个,也可以没有,多个参数使用逗号(,)分隔。<函数代码>是函数实现功能的代码,其中,可使用return语句返回执行结果,如果没有指定返回值,则函数默认返回值是undefined。

下面的代码定义了isPrime()函数,函数需要一个整数参数,并判断其是否质数,是质数时返回true,否则返回false。

JavaScript
<script>
    // 判断n是否为质数
    function isPrime(n) {
        n = parseInt(n);
        if (isNaN(n)) return false;
        if (n < 2) return false;
        let m = Math.ceil(Math.sqrt(n));
        for (let i = 2; i <= m; i++) {
            if (n % i === 0) return false;
        }
        return true;
    }
    //
    let x = 11;
    alert(isPrime(x));
</script>

代码中,isPrime()函数需要带入一个参数n,函数语句中,所有分支都应该返回true或false值。接下来,定义变量x,并赋值为11,使用isPrime()函数判断其是否质数,结果会显示true,即11是质数;这里,可以修改变量x的值来观察运行结果。

参数

定义函数时,参数按定义的顺序分别是没有默认值的参数、有默认值的参数,以及使用...运算符定义的可变长度参数。

下面的代码演示了没有默认值和有默认值参数的应用。

JavaScript
<script>
    function sum(x, y = 0) {
        return x + y;
    }
    //
    alert(sum(10, 99)); // 109
    alert(sum(10)); // 10
</script>

代码中,首先定义了sum()函数,其功能很简单,会返回两个参数相加的和。其中,参数x没有默认值,使用函数时必须指定数据;参数y有默认值0,使用函数时,如果不指定y的数据,则函数中的y就使用默认值0。接下来有两个调用,第一个按参数的顺序设置了x和y的值;第二个调用只设置第一个参数,也就是x的值为10,此时y会使用默认值0。

通过...运算符定义的可变长度参数应放置在参数列表的最后,它可以定义0个或多个参数,下面的代码演示了相关应用。

JavaScript
<script>
    function sum(x, y = 0,...z) {
        let result = x + y;
        z.forEach(function (n) {
            result += n;
        });
        return result;
    }
    //
    alert(sum(1)); // 1
    alert(sum(1,2)); // 3
    alert(sum(1, 2, 3)); // 6
    alert(sum(1, 2, 3, 4, 5)); // 15
</script>

使用...定义的可变长度参数,在函数中反映为一个数组对象,可以通过数组对象的forEach()方法访问其中的数据,并根据需要进行处理。

函数中,有没有方法统一处理所有的参数数据呢?答案是,老办法就是好办法,可以使用arguments对象。函数中,arguments对象是包含了所有参数数据的数组,可以通过数值索引或for...of语句访问其中的数据。下面的代码演示了相关应用。

JavaScript
<script>
    function sum() {
        let result = 0;
        for (let n of arguments) {
            result += n;
        }
        return result;
    }
    //
    alert(sum());  // 0
    alert(sum(1)); // 1
    alert(sum(1,2)); // 3
    alert(sum(1, 2, 3)); // 6
    alert(sum(1, 2, 3, 4, 5)); // 15
</script>

本例,定义的sum()函数没有定义任何参数,函数中统一使用arguments对象处理参数数据;调用函数时,分别指定了0个、1个、2个、3个和5个参数,可以看到,函数中可以使用arguments数组正确地处理不同数量的参数。

调用函数时,如果需要使用数组的元素数据作为参数列表,可以使用...运算符展开,如下面的代码。

JavaScript
<script>
    function sum() {
        let result = 0;
        for (let n of arguments) {
            result += n;
        }
        return result;
    }
    //
    let arr = [1, 2, 3];
    alert(sum(...arr));  // 6
</script>

函数类型

下面的代码,先来看函数是什么类型。

JavaScript
<script>
    function sum() {
        let result = 0;
        for (let n of arguments) {
            result += n;
        }
        return result;
    }
    //
    alert(typeof sum);  // function
</script>

使用typeof运算符获取函数的类型时会返回"function",其真正的类型为Function,也就是说,JavaScript中的函数都是Function类型的对象。

函数对象中还有一些常用的属性和方法,如:

  • name属性,返回函数名。
  • toString()方法,返回函数的定义代码。

下面的代码演示了以变量形式定义的函数。

JavaScript
<script>
    let isLeap = function (year) {
        year = parseInt(year);
        if (isNaN(year)) return false;
        return (year % 400 === 0) ||
            (year % 100 !== 0 && year % 4 === 0);
    }
    //
    alert(isLeap(2020)); // true
    alert(isLeap(2000)); // true
    alert(isLeap(2026)); // false
    alert(typeof isLeap); // functioin
</script>

匿名函数

匿名函数常用于将函数作为参数传递的时候,可以使用function关键字定义函数,也可以=>运算符简化函数的定义。=>运算符的应用,很多开发环境中称为lambda表达式,在运算符左侧定义的变量或变量集合可以看作是函数的参数;运算符的右侧可以看作函数的实现代码,代码的运算结果即是函数的返回值。

在数组、Set和Map对象中的forEach()方法就可以使用匿名函数遍历并处理元素,数组中的map()、filter()、find()等方法也是这样。下面的代码,先看=>运算符的应用。

JavaScript
<script>
    let arr1 = [1, 2, 3];
    let arr2 = arr1.map(e => e * 3);
    alert(arr2);  // 3,6,9
</script>

也可以使用function关键字实现相同的功能,如下面的代码。

JavaScript
<script>
    let arr1 = [1, 2, 3];
    let arr2 = arr1.map(function (e) {
        return e * 3;
    });
    alert(arr2);  // 3,6,9
</script>

自定义函数也可以将函数作为参数,如下面的代码。

JavaScript
<script>
    function mathFactory(x, y, fn) {
        if (typeof fn === "function") {
            return fn(x, y);
        } else {
            return undefined;
        }
    }
    //
    let result = mathFactory(10, 99, (x, y) => x + y);
    alert(result); // 109
    //
    result = mathFactory(10, 99, function (x, y) {
        return x * y;
    });
    alert(result); // 990
</script>

本例,首先定义的mathFactory()函数包括三个参数,x和y为数学计算的数据,第三个参数fn为函数类型,包含两个参数,分别是x和y的数据;其中并没有fn的具体实现代码,需要在调用mathFactory()函数时指定。接下来的第一次调用,使用=>运算符指定两个数据进行加法运算。第二次调用使用函数定义两个数据进行乘法运算。

函数返回可迭代数据

在函数名前添加*符号可以指定函数返回可迭代数据,函数实现代码中,使用yield关键字指定每次迭代返回的数据,如下面的代码。

JavaScript
<script>
    function *counter() {
        for (let i = 1; i < 10; i++) {
            yield i;
        }
    }
    //
    for (let n of counter()) {
        alert(n);
    }
</script>

本例,首先定义了counter()函数,在其函数名称前使用*符号,函数中,使用for语句实现1到9的循环,循环语句中使用yield指定每次迭代返回i的值。接下来,在for...of语句中依次访问counter()函数返回的数据,执行代码会依次显示1到9。