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

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

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

function divide(num1, num2) {
    if (num2 == undefined) {
        num2 = 2;
    }

    return num1 / num2;
}

console.log(divide(12, 4)); // 3
console.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));    // 6
console.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)); // 8
console.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来指定这个函数。

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值