黄子涵

6.3 参数与局部变量

6.3.1 arguments 对象

可以通过在函数内使用 arguments 对象来访问实参。使用方式如代码清单 6.1 所示。

代码清单 6.1 使用 arguments 对象的例子
function hzh1() {
    console.log(arguments.length);
    console.log(arguments[0], arguments[1], arguments[2]);
}
console.log("arguments.length 为实参的数量,值为1");
console.log("arguments[0]的值为7");
console.log(hzh1(7));
console.log("*********************************************");
console.log("arguments.length 为实参的数量,值为2");
console.log("arguments[0] 的值为7,arguments[1] 的值为8");
console.log(hzh1(7, 8));
console.log("*********************************************");
console.log("arguments.length为实参的数量,值为1");
console.log("arguments[0]的值为7");
console.log("arguments[1]的值为8");
console.log("arguments[2] 的值为9");
console.log(hzh1(7, 8, 9));
[Running] node "e:\HMV\JavaScript\JavaScript.js"
arguments.length 为实参的数量,值为1
arguments[0]的值为7
1
7 undefined undefined
undefined
*********************************************
arguments.length 为实参的数量,值为2
arguments[0] 的值为7,arguments[1] 的值为8
2
7 8 undefined
undefined
*********************************************
arguments.length为实参的数量,值为1
arguments[0]的值为7
arguments[1]的值为8
arguments[2] 的值为9
3
7 8 9
undefined

[Done] exited with code=0 in 0.506 seconds

没有相对应的形参的实参也可以通过 arguments 访问。由于能够通过arguments.length 获知实参的数量,因此可以写出所谓的可变长参数函数。而形参的数量则可以通过 Function 对象自身的 length 属性来获得。

虽然 arguments 可以以数组的方式使用,不过它本身并不是数组对象。因此,无法对其使用数组类中的方法。

6.3.2 递归函数

递归函数是一种在函数内对自身进行调用的函数。这种方式被称为递归执行或递归调用。

代码清单 6.2 n 的阶乘(递归函数的例子)
function hzh1(hzh2) {
    if(hzh2 <= 1) {
        return 1;
    } else {
        return hzh2 * hzh1(hzh2 - 1); 
    }
}
console.log("调用函数hzh1:");
console.log(hzh1(5));
[Running] node "e:\HMV\JavaScript\JavaScript.js"
调用函数hzh1:
120

[Done] exited with code=0 in 1.416 seconds

如果递归函数不停地调用自身,运行将不会终止(这与无限循环的情况是一样的,因此俗称为无限递归)。JavaScript 发生无限递归之后的反应取决于实际的运行环境。如果是 SpiderMonkey 的壳层,则会像下面这样发生 InternalError。而在 Java6 附带的 Rhino 中,发生无限递归后则会产生java.lang.OutOfMemoryError 而使 Rhino 停止运行。

// SpiderMonkey 中的无限递归

function hzh() {
    hzh();
}
console.log("调用hzh函数:");
console.log(hzh());
[Running] node "e:\HMV\JavaScript\JavaScript.js"
调用hzh函数:
e:\HMV\JavaScript\JavaScript.js:3
function hzh() {
            ^

RangeError: Maximum call stack size exceeded
    at hzh (e:\HMV\JavaScript\JavaScript.js:3:13)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)

[Done] exited with code=1 in 0.225 seconds

必须在递归函数内部设置递归执行的停止条件判断,这称为终止条件。对于代码清单 6.2 中的情况,在函数开始处会对参数 n 的值是否小于等于 1 进行判断。终止条件的代码并不一定非要写在递归函数的头部,不过一般来说,写在头部更便于阅读。

可以通过循环实现的处理一定也能够通过递归处理来实现,反之也成立。这是因为,递归调用和循环处理两者的本质说到底都是反复执行某一操作。大多数情况下,通过循环来实现的代码会更为简洁明了。而且,在 JavaScript 中递归处理的执行效率并不一定很高。因此,一般情况下最好避免在 JavaScript中使用递归。

能够通过 arguments.callee 来获取正在执行的 Function 对象的引用。这一引用可以在通过没有名字的函数(所谓的匿名函数)来实现递归函数时使用。下面是一个计算 n 的阶乘的具体示例(请注意,在 ECMAScript 第 5 版的静态模式中,arguments.callee 被禁止使用)。

// n 的阶乘(利用arguments.callee)
var hzh = (function(n) {
    if (n <= 1) { 
        return 1; 
    }
    else {
        return n*arguments.callee(n - 1);
    }
})(5);
console.log("输出hzh的值:");
console.log(hzh);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
输出hzh的值:
120

[Done] exited with code=0 in 0.177 seconds
posted @ 2022-05-28 16:58  黄子涵  阅读(48)  评论(0编辑  收藏  举报