默认参数

默认参数允许在函数定义时为参数指定默认值。当调用函数时未传入该参数或传入 undefined 时,参数会使用默认值。这让函数调用更加灵活。

基本语法

在参数后面使用 = 赋值运算符设置默认值。

function greet(name: string, greeting: string = "你好"): string {
  return `${greeting}, ${name}!`
}

console.log(greet("张三"))
console.log(greet("李四", "早上好"))
console.log(greet("王五", undefined))

greeting 参数有默认值 "你好",调用时可以省略。传入 undefined 也会使用默认值。

默认参数的位置

默认参数可以放在参数列表的任意位置,不一定要在最后。

function buildName(firstName: string = "张", lastName: string): string {
  return `${firstName}${lastName}`
}

console.log(buildName(undefined, "三"))
console.log(buildName("李", "四"))

当默认参数不在最后时,必须显式传入 undefined 才能使用默认值。这不如可选参数直观,所以通常把默认参数放在最后。

默认参数与可选参数

默认参数和可选参数都能让参数变为可选,但行为有差异。

function greet1(name: string, greeting?: string): string {
  return `${greeting ?? "你好"}, ${name}!`
}

function greet2(name: string, greeting: string = "你好"): string {
  return `${greeting}, ${name}!`
}

console.log(greet1("张三"))
console.log(greet2("李四"))

console.log(greet1("王五", undefined))
console.log(greet2("赵六", undefined))

主要区别在于传入 undefined 时的行为:可选参数保持 undefined,默认参数使用默认值。

默认值的类型推断

默认值的类型会影响参数的类型推断。

function add(a: number, b = 10): number {
  return a + b
}

console.log(add(5))
console.log(add(5, 20))

b 参数的类型从默认值 10 推断为 number,不需要显式声明类型。

复杂的默认值

默认值可以是表达式或函数调用的结果。

function getConfig(
  apiUrl: string,
  timeout: number = 5000,
  headers: Record<string, string> = {}
): void {
  console.log(`API: ${apiUrl}`)
  console.log(`超时: ${timeout}ms`)
  console.log(`头部:`, headers)
}

getConfig("https://api.example.com")
getConfig("https://api.example.com", 3000, { "Content-Type": "application/json" })

每次调用函数时,对象类型的默认值会创建新的实例,避免了共享引用的问题。

默认值使用其他参数

默认值可以引用前面的参数。

function formatName(firstName: string, lastName: string, fullName: string = `${firstName}${lastName}`): string {
  return fullName
}

console.log(formatName("张", "三"))
console.log(formatName("李", "四", "李四同学"))

fullName 的默认值由 firstNamelastName 组合而成。如果传入了 fullName,则使用传入的值。

默认值与解构

默认参数可以与解构语法结合使用。

interface UserOptions {
  name: string
  age?: number
  email?: string
}

function createUser({ name, age = 0, email = "" }: UserOptions): void {
  console.log(`姓名: ${name}`)
  console.log(`年龄: ${age}`)
  console.log(`邮箱: ${email}`)
}

createUser({ name: "张三" })
createUser({ name: "李四", age: 25, email: "lisi@example.com" })

解构参数的属性可以有默认值,让配置对象的调用更加简洁。

默认值与回调函数

回调函数参数也可以有默认值。

function fetchData(
  url: string,
  callback: (data: any) => void = (data) => console.log(data)
): void {
  setTimeout(() => {
    callback({ url, data: "响应数据" })
  }, 1000)
}

fetchData("https://api.example.com/data")
fetchData("https://api.example.com/users", (data) => {
  console.log("自定义处理:", data)
})

不传回调函数时使用默认的日志输出,传入时使用自定义处理逻辑。

默认值的求值时机

默认值在函数调用时求值,而不是定义时。

let defaultGreeting = "你好"

function greet(name: string, greeting: string = defaultGreeting): string {
  return `${greeting}, ${name}!`
}

console.log(greet("张三"))

defaultGreeting = "早上好"
console.log(greet("李四"))

每次调用函数时,默认值表达式都会重新求值。这意味着默认值可以动态变化。

实际应用

默认参数在实际开发中常用于配置函数和工具函数。

interface RequestOptions {
  method?: "GET" | "POST" | "PUT" | "DELETE"
  headers?: Record<string, string>
  body?: any
  timeout?: number
}

async function request(
  url: string,
  options: RequestOptions = {}
): Promise<any> {
  const {
    method = "GET",
    headers = {},
    body,
    timeout = 5000
  } = options

  console.log(`请求 ${url}`)
  console.log(`方法: ${method}`)
  console.log(`超时: ${timeout}ms`)

  return { data: "响应数据" }
}

request("https://api.example.com/data")
request("https://api.example.com/users", {
  method: "POST",
  body: { name: "张三" }
})

配置对象参数使用空对象作为默认值,内部属性也有各自的默认值。

小结

默认参数为函数参数提供了默认值,让函数调用更加灵活。默认参数可以放在任意位置,默认值可以是表达式或引用其他参数。与可选参数相比,默认参数在传入 undefined 时会使用默认值。合理使用默认参数可以简化函数调用,提高代码的可读性。