跳到主要内容

默认参数

· 阅读需 4 分钟

参数默认值的由来

在开发过程中,经常会使用或封装一些接受动态参数的函数。为了满足实际需求,需要在函数体中书写大量参数处理逻辑:

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)
  1. argument 与实参是绑定的,是动态的,会随着参数的变化而变化
  2. 在严格模式(ES5)下,arguments 与实参的绑定就消失了
  3. 当使用参数默认值时,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,即位于前面的默认参数表达式无法访问位于后面的参数