参数默认值的由来
在开发过程中,经常会使用或封装一些接受动态参数的函数。为了满足实际需求,需要在函数体中书写大量参数处理逻辑:
function substring(start, end) {
end = end || 默认值
}
传统的方式书写麻烦,动态参数越多,代码复杂度也就越大;如果参数值为 0 ,还需额外判断逻辑
ES6
为了解决这个问题,提出了默认参数的语法,表示如果该参数没有传递(undefined
),则使用默认值
function foo(a, b = 2, c = 3) {
console.log(a, b, c)
}
foo() // undefined 2 3
foo(1, null, undefined) // 1 null 3
// 传递的顺序是一一对应的,不会跳跃传递
其中有几个细节可以作为面试题的考点:
arguments
arguments
是一个类数组对象,表示函数调用时传入的实参集合
function mixArgs(a, b = 2) {
console.log(arguments)
console.log(arguments.length)
console.log(a === arguments[0])
console.log(b === arguments[1])
a = 'alpha'
b = 'beta'
console.log(arguments.length)
console.log(a === arguments[0])
console.log(b === arguments[1])e
}
mixArgs(1)
- argument 与实参是绑定的,是动态的,会随着参数的变化而变化
- 在严格模式(ES5)下,arguments 与实参的绑定就消失了
- 当使用参数默认值时,arguments 的行为与严格模式一致
length
length
表示函数声明的形参数量
function foo(a, b = 2, c) { }
console.log(foo.length) // 1
在计算形参 length 时,会只计算默认参数之前的形参个数
比如上面的例子,编译器会觉得这个函数可以只传一个参数 a,所以 foo.length 为 1
默认值表达式
默认参数不一定是字面量,也有可能是一个表达式。这个表达式只在需要用到默认值时,才会去调用
let n = 1
function getValue() {
return n++
}
function foo(a, b = getValue()) {
console.log(a, b)
console.log('n:', n)
}
foo(1, 10) // 1 10 n:1 不需要默认值,不会去调用默认值表达式
foo(1) // 1 1 n:2
foo(1, 10) // 1 10 n还是2
foo(1) // 1 2 n:3
foo(1) // 1 3 n:4
参数默认值的 TDZ
任何变量都会发生变量提升,只不过 let const
会有 TDZ
的现象,因为在语法层面上,不希望开发者在变量声明前去使用
function getValue(v) {
return v * 2
}
function foo(a, b = getValue(a)) {
console.log(a, b)
}
foo(1) // 1 2
foo(2) // 2 4
function bar(a = getValue(b), b) {
console.log(a, b)
}
bar(undefined, 1) // error
bar(undefined, 2) // error
参数默认值与 let const 一样,都会产生 TDZ,即位于前面的默认参数表达式无法访问位于后面的参数