不一样的JavaScript(4)——函数参数

来源:互联网 发布:微信公众号假数据推广 编辑:程序博客网 时间:2024/06/05 09:14

1. 在Javascript中,如果函数申明时的参数个数和函数调用时的参数个数不相等,并不会引起语法错误。

1.1 如果函数声明时的参数个数多于函数调用时的参数个数,在调用时没有设置的参数会被自动设为undefined。

function divide(num1, num2) {    if (num2 == undefined) {        num2 = 2;    }    return num1 / num2;}console.log(divide(12, 4)); // 3console.log(divide(12));    // 6

在上述代码中,我们声明一个有两个参数num1和num2的函数divide。当我们用参数12和4调用函数divide时,这两个值分别赋予num1和num2。当我们只设置一个参数12时,这个数值被赋予参数num1,而num2被设为undefined。

上述代码相当于把函数divide的第二个参数num2设置了一个缺省值2。当在函数调用的时候没有给num2传递一个数值,那么就将使用缺省值2。下面的C++代码和它的功能类似:

int divide(int num1, int num2=2) {    return num1 / num2;}

1.2 如果函数声明是的参数个数少于函数调用时的参数个数,我们可以通过arguments得到函数的所有的参数。argument是存在于每个函数体内的一个数组,用来存储函数调用时传递的参数。

function sum() {    var total = 0;    for (var i = 0; i < arguments.length; ++i) {        total += arguments[i];    }    return total;}console.log(sum(1, 2, 3));    // 6console.log(sum(1, 2, 3, 4)); // 10

 上述代码在声明函数sum的时候,参数列表中没有一个参数,但在函数体内用argument得到在函数调用时实际传递的参数,在累加所有参数并返回它们的和。所以当传入三个参数1、2、3时,得到它们的和6;当传入4个参数1、2、3、4时,得到它们的和10。

1.3 由于Javascript能给函数传入不同数目的参数,因此在Javascript不能和很多语言一样实现多态(定义多个函数名相同但是参数的数目或者类型不同的函数)。在Javascript,如果一个函数名被定义多次,最后一次将覆盖前面的定义。例如:

function add(num1, num2, num3) {    console.log(num1 + num2 + num3);}function add(num1, num2) {    console.log(num1 + num2);}add(1, 2, 3);
在上述代码中,我们定义了两个叫add的函数。第二个(只有两个参数)函数的定义会覆盖前面一个定义。因此当我们用三个参数调用函数add的时候,第三个参数会被抛弃。因此最终的输出结果是3。

2. argument有个属性叫callee,它指向拥有argument的函数。这个属性经常用在递归函数之中。

我们先来看一个递归函数:

function power(base, exponent) {    if (exponent == 1) {        return base    }    return base * power(base, exponent - 1);}console.log(power(2, 3)); // 8

在上述代码中,函数power用来用求base的exponent次方。当我们在其他地方不再使用名字power去定义其他函数和变量的时候,上述代码是没有问题的。但我们也要注意到,这个递归函数的功能和power这个名字是紧密耦合在一起的。一旦我们重新定义power这个名字的意义,就有可能导致问题。

解决这个问题的方法就是用argument的callee属性去解耦。上面的代码可以改写成如下代码:

function power(base, exponent) {    if (exponent == 1) {        return base;    }    return base * arguments.callee(base, exponent - 1);}var realPower = power;power = function (num1, num2) {    return num1 + num2;}console.log(realPower(2, 3)); // 8console.log(power(2, 3));     // 5

在修改之后的代码中,我们用argument.callee递归定义函数power。在定义一个新的变量realPower指向该函数之后,我们让power指向新的函数。这样即使函数power的功能已经发生改变,但乘方的功能仍然得以保全。
另外一个需要用到argument.callee的场景,就是我们需要在一个匿名函数体的内部需要调用函数自身。由于函数是匿名的,我们不能在代码中通过指明函数名来调用该函数。这个时候,我们可以使用argument.callee。比如如下代码:

var numbers = [1, 2, 3, 4, 5];setTimeout(function () {    console.log(numbers.shift());    // if there are some elements left, set another timer    if (numbers.length > 0) {        setTimeout(arguments.callee, 100);    }    else {        console.log("finished");    }}, 100);

上述代码的作用是每隔100毫秒打印出数组中剩余数字的第一个。

在第一个调用setTimeOut函数的地方,我们定义了一个匿名函数作为setTimeOut的参数。当数组中还剩余数字是,我们需要重新调用setTimeOut以便打印下一个数字。在第二次调用setTimeOut的时候,它的第一个参数应该是和第一次调用时的参数是同一个函数。但由于我们为setTimeOut函数定义的参数是一个匿名函数,我们不知道它的名字。这个时候我们可以通过argument.callee来指定这个函数。

 

原创粉丝点击