函数 - 理解参数
ECMAScript函数的参数与大多数其他语言中的函数参数有所不同。ECMAScript函数不介意传递进来多少个参数,也不在乎传进来参数是什么数据类型,也就是说,即使你定义的函数只接收两个参数,在调用这个函数时也未必一定要传递两个参数。可以传递一个,三个甚至不传递参数,而解析器永远不会有什么怨言,之所以会这样,原因是ECMAScript中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。如果这个数组中不包含任何元素,无所谓;如果包含多个元素,也没问题。实际上,在函数体内可以通过arguments对象来访问这个参数数组,从而获取传递参数的每一个参数。
其实,arguments对象只是数组类似(它并不是Array实例),因为可以使用方括号语法访问它的一个元素(即第一个元素是arguments[0],第二个元素是argument[1],以此类推),使用length属性来确定传递进来多少个参数。在前面的例子中,sayHi()函数的第一个参数的名字叫name,而该参数的值也可以通过访问arguments[0]来获取。因此,那个函数也可以像下面这样重写,即不显式地使用命名参数:
function sayHi(){ alert("hello"+arguments[0]+","+arguments[1]); }
这个重写后的函数中不包含命名的参数。虽然没有使用name和message标识符,但函数的功能依旧。这个事实说明了ECMAScript函数的一个重要特点:命名参数只提供便利,但不是必须的。另外,在命名参数方面,其他语言可能事先创建一个函数签名,而将来的调用必须与该签名一致。但在ECMAScript中,没有这些条条框框,解析器不会验证命名参数。
通过访问arguments对象的length属性可以获知有多少个参数传递给了函数。下面这个函数会在每次被调用的时,输出传入其中的参数个数:
function howManyArgs(){ alert(arguments.length); } howManyArgs("string",45)//2 howManyArgs();//0 howManyArgs(12)//1
执行以上代码会依次出现3个弹出框,分别显示2,0和1。由此可见,开发人员可以利用这一点让函数能够接收任意个参数并分别实现适当的功能。请看下面例子:
function doAdd(){ if(arguments.length==1){ alert(arguments[0]+10); }else if(arguments.length==2){ alert(arguments[0]+arguments[1]); } } doAdd(12);//22 doAdd(30,25);//55
关于arguments的行为,还有一点比较有意思。那就是它的值永远与对应命名参数的值保持同步。例如:
function doAdd(num1,num2){ arguments[1]=10; alert(arguments[0]+num2); }
每次执行这个doAdd()函数都会重写第二个参数,将第二个参数的值修改为10。因为arguments对象中的值会自动反映到对应的命名函数,所以修改arguments[1],也就修改了num2,结果他们的值都会变成10。不过,这并不是说读取这两个值会访问相同的内存空间;它们的内存空间是独立的,另外还要记住,如果只传人了一个参数,那么为arguments[1]设置的值不会反应到命名参数中。这是因为arguments对象的长度是由传人的参数决定的,不是由定义函数时的命名参数决定的。
关于参数还要记住最后一点:如果没有传递值的命名参数将自动被赋予undefined值。这就跟定义了变量但又没有初始化一样。例如,如果只给doAdd()函数传递了一个参数,则num2中就会保存undefined只值。
严格模式对如果使用arguments对象做出了一些限制。首先,像2前面例子中那样的赋值会变得无效。也就是说,即使arguments[1]设置为10,num2的值仍然还是undefined。其次,从写arguments的值会导致语法错误(代码将不会执行)。