前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 参数按值传递还是按引用传递
JavaScript函数与作用域

JavaScript 传参是按值传递还是按引用传递?传对象进去改了会怎样?

JavaScript 只有按值传递。传对象时,复制给形参的是「指向这个对象的引用」,函数里和外面拿着两把指向同一个对象的钥匙。所以改属性外面能看到,但把形参换成一个新对象,外面的变量不受影响。

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

复制的是钥匙,不是房子

  1. 原始值数字、字符串等直接复制一份,函数里怎么改都和外面无关
  2. 改属性形参和外部变量指向同一个对象,改了双方都看得到
  3. 重新赋值只是让形参指向别的对象,外部变量还指向原来那个

const 只是不让变量重新赋值,不会冻结对象。const 声明的对象传进函数,属性照样能被改掉。

核心回答

先记住这个答案

JavaScript 的参数都是按值传递的,调用时把实参的值复制一份给形参。原始值复制的是值本身;对象复制的是「指向这个对象的引用」,所以形参和外部变量指向同一个对象。在函数里改对象的属性,外面能看到;给形参重新赋一个新对象,只是形参换了指向,外面的变量不变。有人把这种行为叫 call by sharing(共享传递),本质上还是按值传递,只不过这个值是引用。

  • 所有参数都按值传递,对象传的是引用的副本
  • 函数里改属性,外面能看到
  • 给形参重新赋值,外面不受影响
  • 展开运算符 {...obj} 只拷贝一层

一个函数里同时做两件事

先改传进来的对象的属性,再给形参赋一个新对象,看外面的变量变成了什么。

改属性和重新赋值的区别JavaScript
function update(profile) {
  profile.name = 'Grace' // 改的是共享的那个对象
  profile = { name: 'Lin' } // profile 指向新对象,和外面断开
  console.log(profile.name)
}
const user = { name: 'Ada' }
update(user)
console.log(user.name)
查看输出与解释
Lin
Grace

打个比方:user 手里有一把房子的钥匙,调用时复制了一把给 profile。第一行 profile 用钥匙开门,把房子里的名字改成了 Grace,user 回家当然也看到 Grace。第二行 profile 把自己的钥匙换成了另一套房子的,这和 user 手里那把钥匙没有关系,所以外面还是 Grace,不是 Lin。

为什么说不是按引用传递

真正的按引用传递,是把变量本身交给函数,函数里给形参赋值,外面的变量也跟着变。C++ 的引用参数、C# 的 ref 就是这样。用 swap 函数一试就知道 JavaScript 不是。

swap 交换不了外面的变量JavaScript
function swap(a, b) {
  const t = a
  a = b // 只改了形参 a
  b = t // 只改了形参 b
}
let x = { v: 1 }
let y = { v: 2 }
swap(x, y)
console.log(x.v, y.v) // 外面没变
查看输出与解释
1 2

如果是按引用传递,x 和 y 应该被交换。实际上 a 和 b 只是两份钥匙的拷贝,函数里怎么换都不影响外面的 x 和 y。所以说「对象按引用传递」是一种容易让人推错的简化说法,面试时最好讲准确:按值传递,对象的值是引用。

项目里怎么避免函数改掉传入的对象

函数悄悄改了传入的对象,是很多诡异 bug 的来源。比如 React 里直接改 state 对象,引用没变,组件就不会重新渲染。通用的做法是不改原对象,返回一个新对象。

展开运算符只拷贝一层JavaScript
function rename(user, name) {
  return { ...user, name } // 顶层是新对象
}
const u = { name: 'Ada', tags: ['a'] }
const u2 = rename(u, 'Bob')
u2.tags.push('b') // tags 数组还是共享的
console.log(u.name, u.tags.length)
查看输出与解释
Ada 2

name 没有被改,因为 u2 是新对象。但 tags 被拷贝过来的只是数组的引用,u2.tags 和 u.tags 还是同一个数组,push 之后两边都变成了两个元素。所以做不可变更新时,改到哪一层,就要把那一层也复制一份,比如 { ...user, tags: [...user.tags, 'b'] }。

回答前,多想一步

容易答错的地方

说「对象是按引用传递的」
这个说法会让人以为给形参重新赋值能改掉外面的变量,上面的 swap 例子就会推错。准确的说法是:参数都按值传递,对象的值是一个引用。
以为 const 声明的对象不能被改
const 限制的是变量不能重新赋值,对象内部的属性照样可以改。传给函数之后,函数也能改它的属性。想让对象不可改,要用 Object.freeze,而且它也只冻结一层。
试着用自己的话回答

面试官还会怎么问?

数组作为参数也一样吗?

一样。函数里 push、splice、直接改下标,外面的数组都会变;给形参赋一个新数组,外面不变。slice、map、filter 返回新数组,不改原数组,但里面的对象元素仍是共享的。

怎么深拷贝一个对象?

现在首选 structuredClone(obj),能处理嵌套对象、Date、Map、Set 和循环引用。但它不支持函数和 DOM 节点,遇到会抛错,类的原型也不会保留。JSON.parse(JSON.stringify(obj)) 会丢掉 undefined、函数,并把 Date 变成字符串,只适合纯数据。

字符串是原始值,为什么还能调用方法?

调用 'abc'.toUpperCase() 时,引擎临时把字符串包装成 String 对象,调用完就丢掉。字符串本身不可变,所有字符串方法都返回新字符串,所以传字符串进函数,函数里怎么操作都改不了外面的值。

从一道题,走向一组知识

把知识连起来

函数与作用域

把对象方法传给 setTimeout,为什么 this 就丢了?怎么解决?

把 obj.method 当参数传出去时,传的也只是函数这个值,obj 没有跟着传过去,所以 this 会丢。

函数与作用域

for 循环里用 var,setTimeout 为什么打印出来全是同一个数?

闭包记住的是变量本身而不是值的快照,和这里区分「改对象」和「改变量」是同一种思路。

参考资料

  • JavaScript:函数与作用域参考资料

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

本题目录
  1. 先记住这个答案
  2. 一个函数里同时做两件事
  3. 为什么说不是按引用传递
  4. 项目里怎么避免函数改掉传入的对象
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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