本文深入分析JavaScript中函数作为一等公民的特性,重点讲解参数传递的灵活性。通过实例演示arguments数组的使用、长度判断及格式化输出模拟,并探讨arguments.callee在递归函数中的稳定性优势,帮助开发者避免函数重定义导致的引用错误。
JavaScript函数作为标识符的特性
在JavaScript中,函数被视为一种特殊的标识符,它可以被赋值给新的变量,并通过该变量进行调用。这一机制类似于C语言中的函数指针,但存在差异。具体实现如以下代码所示:
代码如下:
function myfun() {
alert("funcation call");
}
var fun = myfun;
fun(); 灵活参数传递与arguments数组应用
另一个值得关注的特性是,JavaScript中的函数参数并不要求严格匹配。根据常规编程经验,若定义函数 fun(aa,bb),调用时通常需传递两个参数。然而在JavaScript中,可以传递任意数量的参数(如1个、3个等)。JS的参数传递机制不完全依赖声明时的形式参数,每次调用函数时,系统都会生成一个名为 arguments 的数组,其中存储了所有传入的实际参数。利用该数组,即使函数声明时未指定形式参数,也能正常处理输入,示例代码如下:
代码如下:
function args() {
if (arguments[0] != undefined) {
alert(arguments[0]);
}
}
args(); //什么也不输出
args("hehe"); //弹出参数值 如上所示,arguments 数组的下标从 0 开始,依次对应传入的每个参数。若指定位置无对应参数,则该位置值为 undefined。
我们可以使用 arguments.length 来判断传入参数的个数,这一特性在某些场景下非常实用。例如,利用此特性可以模拟C语言的 printf 函数功能:
代码如下:
function format() {
if (arguments.length == 0) {
return "";
}
var formatter = arguments[0];
for (var i = 1; i < arguments.length; i++) {
formatter = formatter.replace(new RegExp("\\{"+(i-1)+"\\}","gm"), arguments[i]);
}
return formatter;
}
alert(format("Hello {0},this is the fetures of {1}!","world","javascript")); 利用arguments.callee优化递归函数
上述代码简单实现了格式化输出的基本功能。有兴趣的开发者还可以进一步优化。最后补充一点,arguments 对象还有一个 callee 属性,它指向当前正在执行的函数。该属性在某些特定场景下具有实际价值,考虑以下代码:
代码如下:
function sum(num) {
if (num == 1) {
return num;
} else {
return num + sum(num - 1);
}
}
var mysum = sum;
alert(mysum(5)); //输出 15
sum = function() { return 1; };
alert(mysum(5)); //输出 6 这是一个递归求和函数。初始时 mysum 与 sum 指向同一函数体。若在程序执行过程中 sum 的函数体被重新赋值,再次调用 mysum 的结果将发生变化。若将函数修改为以下形式,则可避免此类问题:
代码如下:
function sum(num) {
if (num == 1) {
return num;
} else {
return num + arguments.callee(num - 1);
}
}
var mysum = sum;
alert(mysum(5)); //输出 15
sum = function() { return 1; };
alert(mysum(5)); //输出 15 如上述代码所示,无论外部引用如何改变,callee 始终指向当前被调用的函数。在使用JavaScript编写递归逻辑时,这一点值得注意。虽然此类错误不常发生,但一旦引发,排查原因较为困难,可能耗费大量时间。
会出现这种错误,但是如果出了是不太容易发现原因的,会浪费不少时间。