这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战
前言
今天我们来讲一讲参数吧,之前我们对参数都是一个模糊的概念,今天我们主要详细来讲一讲。
理解参数
ECMAScript 函数的参数跟大多数其他语言不同。 ECMAScript 函数既不关心传人的参数个数,也不关心这些参数的数据类型。定义函数时要接收两个参数,并不意味着调用时就传两个参数。你可以传一个、三个,甚至一个也不传,解释器都不会报错。
之所以会这样,主要是因为 ECMAScript 函数的参数在内部表现为一个数组。函数被调用时总会接收一个数组,但函数并不关心这个数组中包含什么。如果数组中什么也没有,那没问题;如果数组的元素超出了要求,那也没问题。事实上,在使用 function 关键字定义(非箭头)函数时,可以在函数内部访问 arguments 对象,从中取得传进来的每个参数值。 arguments 对象是一个类数组对象(但不是 Array 的实例),因此可以使用中括号语法访问其中的元素(第一个参数是 arguments [0],第二个参数是 arguments [1])。而要确定传进来多少个参数,可以访问 arguments . length 属性。 在下面的例子中, sayHi ()函数的第一个参数叫 name :
function sayHi ( name , message ){
console . log (" Hello "+ name +"," "+ meSSage );
}
可以通过 arguments [0]取得相同的参数值。因此,把函数重写成不声明参数也可以:
function sayHi(){
console . log (" Hello "+ arguments [0]+","+ arguments [1]);
}
在重写后的代码中,没有命名参数。 name 和 message 参数都不见了,但函数照样可以调用。这就表明, ECMAScript 函数的参数只是为了方便才写出来的,并不是必须写出来的。与其他语言不同,在 ECMAScript 中的命名参数不会创建让之后的调用必须匹配的函数签名。这是因为根本不存在验证命名参数的机制。
也可以通过 arguments 对象的 length 属性检查传入的参数个数。下面的例子展示了在每调用一个函数时,都会打印出传人的参数个数:
function howManyArgs (0(
console . log ( arguments . length );
howManyArgs (" string ",45);//2
howManyArgs (); //0
howManyArgs (12); //1
这个例子分别打印出2、0和1(按顺序)。既然如此,那么开发者可以想传多少参数就传多少参数。 比如:
function doAdd (){ )( spAwen )
if ( arguments . length ===1){ epAwe0E8T69Y、 aid
console . log ( arguments [0]+10);
) else if ( arguments . length ===2){
console . log ( arguments [0]+ arguments [1]);
doAdd (10); //20
doAdd (30,20);//50
这个函数 doAdd (在只传一个参数时会加10,在传两个参数时会将它们相加,然后返回。因此 doAdd (10)返回20,而 doAdd (30,20)返回50。虽然不像真正的函数重载那么明确,但这已经足以弥补 ECMAScript 在这方面的缺失了。
还有一个必须理解的重要方面,那就是 arguments 对象可以跟命名参数一起使用,比如:
function doAdd ( numl ,num2){
1f( argument В. length в1){ cosole . log ( numl +10);
}
else if ( argument В. length 三国В2){ console . log ( arguments [0]+num2);
在这个 doAdd ()函数中,共同使用了两个命名参数和 arguments 对象。命名参数保存着与num1 arugments [ O ]一样的值,因此使用谁都无所谓。(同样,num2也保存着跟 arguments [1]一样的值。) arguments 对象的另一个有意思的地方就是,它的值始终会与对应的命名参数同步。来看下面的例子:
funct ion doAdd (num1,num2){
arguments [1]=10;\
console . log ( arguments [ o ]+num2);
}
这个 doAdd ()函数把第二个参数的值重写为10。因为 arguments 对象的值会自动同步到对应的命名参数,所以修改 arguments [1]也会修改num2的值,因此两者的值都是10。但这并不意味着它们都访问同一个内存地址,它们在内存中还是分开的,只不过会保持同步而已。但是,这种同步是单向的:修改命名参数的值,不会影响 arguments 对象中相应的值。另外还要记住一点:如果只传了一个参数,然后把 arguments [1]设置为某个值,那么这个值并不会反映到第二个命名参数。这是因为 arguments 对象的长度是根据传人的参数个数,而非定义函数时给出的命名参数个数确定的。
对于命名参数而言,如果调用函数时没有传这个参数,那么它的值就是 undefined 。这就类似于定义了变量而没有初始化。比如,如果只给 doAdd (传了一个参数,那么num2的值就是 undefined 。 严格模式下, arguments 会有一些变化。首先,像前面那样给 arguments [1]赋值不会再影响num2的值。就算把 arguments [1]设置为10,num2的值仍然还是传人的值。其次,在函数中尝试重写 arguments 对象会导致语法错误。(代码也不会执行。 箭头函数中的参数 如果函数是使用箭头语法定义的,那么传给函数的参数将不能使用 arguments 关键字访问,而只能通过定义的命名参数访问
虽然箭头函数中没有 arguments 对象,但可以在包装函数中把它提供给箭头函数:
function foo (){1et bar =()=>{
console . log ( arguments [0]);//5;
bar ();
foo (5);
}
注意: ECMAScript 中的所有参数都按值传递的。不可能按引用传递参数。如果把对象为参数传递,那么传递的值就是这个对象的引用。