众所周知,JavaScript是一门神奇的语言,这一点体现在很多地方,而本篇专注于讲述其函数的参数的特性
理解JavaScript的参数
JS的参数跟大多数其他语言不同,它的函数不在乎你传的参数的个数,也不在乎它是什么数据类型,基本类型,对象,函数等等它一点也不挑嘴,来者不拒。
而之所以会这样,是因为在JS中函数的参数在内部表现为一个数组,在函数被调用时,会接收一个数组,数组里面有什么的无所谓,有没有也无所谓。在使用function定义函数(箭头函数是个特例)时,可以在函数内部访问arguments对象来取得传进来的每个参数值
arguments是一个类数组对象,注意这个类字,表明它不是真正的Array实例,但也能使用[]访问其中的元素,也具有length属性来获得传进来了多少个参数
function sayHi(name, message){
console.log(`Hello, ${name}, ${message}`)
}
function sayHi(){
console.log(`Hello, ${arguments[0]}, ${arguments[1]}`)
}
这两种写法最终的效果是一样的,这也表明,JS的函数的参数是为了方便才写出来的,并不是必须的。JS的命名参数不会创建让之后的调用必须匹配的函数签名,因为根本不存在验证命名参数的机制。
没有重载
前面提到了JS函数没有签名,因为参数是由包含零个或者多个值的数组表示的。没有函数签名,自然就没有重载。如果在JS中定义了两个同名函数,则后定义的会覆盖先定义的(明明是我先来的~bushi)
而借助JS函数参数无限制这一特性,虽然不像真正的函数重载那么明确,但这已经足够弥补JS在这一方面的缺失了。
还有很重要的一点——arguments对象可以跟命名参数一起使用,两者会对应顺序保存相同的值,而且arguments有一个有意思的地方是,它的值会始终跟对应的命名参数同步
function add(num1, num2){
arguments[1] = 10
console.log(arguments[0] + num2)
}
在该函数中,利用这个特性通过arguments将num2的值修改为了10。但这并不意味着两者访问同一个内存地址,它们是分开的,但保持着同步。要注意的一点是:如果只传了一个参数,然后将arguments[1]的值修改了,这个值并不会反映到第二个命名参数,因为arguments对象的长度是根据传入的参数个数而非定义函数时给出的命名参数个数来确定的。
对于命名参数而言,如果调用时没有传这个参数,那么它的参数就是 undefined。
在严格模式中,arguments的该特性会失效,修改arguments并不会反映到对应的命名参数上,而且在函数中尝试重写arguments对象会导致语法错误,代码也不会执行。
箭头函数的参数
如果函数是通过箭头语法定义的,那么它将不会有arguments对象,只能通过定义的命名参数去访问传进来的参数。