前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库默认参数求值时机 能否引用前面参数
JavaScript函数与作用域

函数默认参数是什么时候计算的?能用前面的参数当默认值吗?

默认参数在每次调用时才计算,而且只在对应的实参是 undefined 时才计算。参数从左到右依次初始化,所以后面参数的默认值可以用前面的参数,反过来用会报 ReferenceError。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#函数与作用域
先看核心答案读代码示例
理解线索

默认值什么时候算、按什么顺序算

  1. 触发条件没传或显式传 undefined,才执行默认值表达式
  2. 初始化顺序从左往右一个个初始化,右边的可以依赖左边的
  3. 可见范围能读前面的参数和外层变量,读不到函数体里的声明

默认值每次都重新算,所以 list = [] 每次调用拿到的都是新数组。这一点和 Python 正好相反,Python 的默认值只在定义时算一次,会被多次调用共享。

核心回答

先记住这个答案

默认参数是在每次调用函数时才计算的,不是定义函数时算好存起来。只有对应的实参没传,或者传的是 undefined,默认值表达式才会执行,传 null、0、空字符串都不会触发。参数按从左到右的顺序初始化,所以写 function f(a, b = a * 2) 没问题,右边的参数可以用左边的。反过来 function f(a = b, b) 会报 ReferenceError,因为初始化 a 时 b 还没初始化。

  • 每次调用都重新计算,不是定义时算一次
  • 只有 undefined 触发默认值,null 不会
  • 从左到右初始化,后面的参数能用前面的
  • 默认值表达式里读不到函数体里声明的变量

用一个计数器看清求值时机

把默认值写成一个会计数的函数调用,就能直接看出它什么时候执行、执行了几次。

默认值表达式每次按需执行JavaScript
let calls = 0
const nextId = () => ++calls

function create(id = nextId(), label = `item-${id}`) {
  return label
}

console.log(create()) // id 没传,执行 nextId()
console.log(create()) // 再执行一次,不是复用上次的值
console.log(create(99)) // 传了 id,nextId 不执行
console.log(create(null)) // null 不触发默认值
console.log(calls)
查看输出与解释
item-1
item-2
item-99
item-null
2

前两次调用没传 id,所以 nextId() 各执行一次,分别得到 1 和 2。第三次传了 99,默认值表达式根本不执行。第四次传了 null,null 不是 undefined,也不执行,于是 id 就是 null,模板字符串里显示成 item-null。最后 calls 是 2,说明默认值只在需要时才算。 label 的默认值用到了 id,这就是「后面的参数引用前面的参数」。执行到 label 时,id 已经初始化完了,所以可以读。

默认值表达式能看到哪些变量

有默认参数的函数,参数列表会有一个自己的作用域,夹在外层作用域和函数体中间。默认值表达式能看到的,是这个参数作用域和外层。

读不到函数体里的变量
比如 function f(a = x) { const x = 1 },调用 f() 时,默认值里的 x 找的不是函数体里那个 x。函数体的变量要等参数全部初始化完才创建,默认值表达式看不到它。如果外层也没有 x,就会报 ReferenceError。
引用后面的参数为什么报错
所有参数的名字在进入函数时就已经登记了,但要按顺序初始化。初始化 a 时去读 b,b 虽然存在,却处于还没初始化的状态,这段时间叫暂时性死区,读它就会报错。同理 function f(x = x) 也会报错,右边的 x 读的是正在初始化的参数 x 自己,而不是外层的 x。
回答前,多想一步

容易答错的地方

以为传 null 也会用默认值
只有 undefined 触发默认值。后端接口经常返回 null,直接传给带默认值的函数,拿到的就是 null。如果想把 null 也当作没传,要在函数体里写 value ?? defaultValue。
以为默认值只计算一次
有人担心 function add(item, list = []) 多次调用会共用一个数组,那是 Python 的行为。JavaScript 每次调用都会重新执行 [],每次都是新数组,不会互相污染。反过来,如果默认值是一个开销很大的计算,每次调用都会重新算一遍,需要自己做缓存。
试着用自己的话回答

面试官还会怎么问?

默认参数会影响 arguments 吗?

不会。arguments 只记录调用时真正传进来的实参。function f(a = 1) {} 调用 f() 时,a 是 1,但 arguments.length 是 0。另外只要函数用了默认参数,arguments 和形参之间就不再联动,改 a 不会影响 arguments[0]。

默认参数对 fn.length 有什么影响?

length 只统计第一个带默认值的参数之前的形参个数。function f(a, b = 1, c) {} 的 length 是 1,c 虽然没有默认值,但在 b 后面,也不计入。

解构参数的默认值怎么写?

function f({ page = 1, size = 10 } = {})。里面的默认值负责对象里缺少的字段,外面的 = {} 负责整个参数都没传的情况。少了外面这个 = {},调用 f() 时会因为解构 undefined 报 TypeError。

从一道题,走向一组知识

把知识连起来

函数与作用域

bind 和 call、apply 有什么区别?bind 返回的函数有什么特点?

bind 预置参数和默认参数都能给函数提供初始值,但 bind 是固定死的,默认参数每次调用重新计算。

函数与作用域

剩余参数 ...args 和 arguments 有什么区别?为什么推荐用前者?

默认参数和剩余参数都会影响 fn.length,而且剩余参数本身不能设置默认值。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • Default parameters

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 用一个计数器看清求值时机
  3. 默认值表达式能看到哪些变量
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑