JS 的 function 按值传递

在计算机科学中:

  • 按值传递(pass by value):函数接收参数的副本,修改副本不影响原始值。
  • 按引用传递(pass by reference):函数接收对原始变量的直接引用,修改参数会直接影响原始变量。

JavaScript中:

  • 基本类型(如number, string, boolean)是按值传递。函数内部对参数的修改不会影响到外部变量;
  • 复杂类型(包括对象,数组、函数等)传递的是对象引用的副本(即按共享传递/按对象引用副本传递),而不是按引用传递。【实际数据存储在堆中,栈中保存的是指向这些数据的引用】

关键区别:

  • 如果JavaScript是真正的按引用传递,那么在函数内重新赋值参数(如obj = {new: 'object'})会改变外部的引用,但实际不会。

  • 实际传递的是引用的值(一个内存地址的副本),所以:

    • 修改对象属性会影响原对象(因为通过相同的地址访问)。

    • 但重新赋值参数变量不会影响外部引用(因为只是修改了局部变量指向的新地址)。

1.对象是通过引用的值传递的

js 复制代码
// 创建一个对象
const originalData = { count: 0 };

// 复制"引用"而非值-
const instanceA = originalData;
const instanceB = originalData;

// 修改实例A会影响原始对象
instanceA.count = 5;

console.log(originalData.count); // 输出 5
console.log(instanceB.count);    // 输出 5 (共享同一对象)

2. 函数是通过值传递的

在JavaScript中,所有函数参数传递都是按值传递的。但是,当传递的是对象时,传递的值实际上是对象的引用(即内存地址)的副本,而不是对象本身。

因此,对于基本类型(如number, string, boolean等),传递的是值的副本,修改参数不会影响原始值。

对于对象类型(包括数组、函数等),传递的是引用的副本,因此通过参数修改对象的属性会影响到原始对象,但如果重新赋值整个参数(即改变引用的指向)则不会影响原始对象。

📌 核心结论

类型 传递方式 修改原始值的影响
基本类型 按值传递 ❌ 不影响原变量
引用类型 按共享传递 ✅ 修改属性影响原对象
❌ 重赋值不影响原引用
js 复制代码
function updateProperty(obj) {
  // 此处 obj 是原始引用的拷贝,不是原始引用本身
  obj.value = 10; // ✅ 修改原对象属性
}

function replaceObject(obj) {
  obj = { value: 99 }; // ❌ 重赋值不影响原引用
}

const myObj = { value: 5 };

updateProperty(myObj);
console.log(myObj.value); // 输出 10(属性被修改)

replaceObject(myObj);
console.log(myObj.value); // 输出 10(对象未被替换)

✅ 总结

JavaScript 严格遵循按值传递

  • 基本类型:传递值副本
  • 引用类型:传递引用地址的副本(常被误称为"按共享传递")

3. 按值传递,用堆栈来理解

注意:在JavaScript中,变量存储分为栈(stack)和堆(heap)两部分。

基本类型值存储在栈中,而对象存储在堆中,变量名(标识符)存储在栈中,其值对于基本类型是实际值,对于对象则是堆中内存地址的引用。

例子还是第2点的例子:

const myObj = { value: 5 };

  1. 在堆(heap)中创建一个对象 { value: 5 },假设其内存地址为 0x100
  2. 在栈(stack)中为变量 myObj 分配空间,存储的是堆中对象的地址 0x100(即引用)。

此时,栈中:

变量名
myObj 0x100

堆中:

地址:0x100

内容:{ value: 5 }


updateProperty(myObj);

function updateProperty(obj) { obj.value = 10; }

  • 调用函数时,将 myObj 的值(即地址 0x100)复制一份,传递给函数参数 obj
  • 所以在函数内部,obj 同样指向堆中的地址 0x100

栈(在函数执行时):

变量名
myObj 0x100
obj 0x100

堆中:

地址:0x100

内容:{ value: 10 } // 被修改了

所以,当我们在函数内通过 obj.value = 10 修改属性时,是通过地址 0x100 找到堆中的对象并修改的,因此原对象被修改。


replaceObject(myObj);

function replaceObject(obj) { obj = { value: 20 }; }

  • 传递时,将 myObj 的值(0x100)复制给 obj(函数参数)。

  • 在函数内部,执行 obj = { value: 20 } 时:

    1. 在堆中创建一个新对象 { value: 20 },假设地址为 0x200
    2. 将函数参数 obj 的值改为 0x200(注意:这改变的是栈中函数参数obj存储的地址,而不是原来0x100地址处的对象)。

此时,栈(在函数reassign内部):

变量名
obj 0x200

堆中:

地址:0x100 -> { value: 5 } // 未被改变

地址:0x200 -> { value: 20 }

函数执行完毕后,栈中函数参数obj被释放,而全局变量myObj依然指向0x100,所以原对象没有被改变。


总结:

  • 在函数内部修改对象的属性,会影响到原对象,因为通过相同的引用地址操作堆中的同一个对象。
  • 在函数内部重新赋值参数(即改变参数变量的指向),不会影响外部变量,因为传递的只是外部变量所存储的地址值的副本,函数内部改变的是这个副本(指向了一个新对象),而外部变量仍然指向原对象。
相关推荐
月阳羊5 小时前
【硬件-笔试面试题】硬件/电子工程师,笔试面试题-26,(知识点:硬件电路的调试方法:信号追踪,替换,分段调试)
笔记·嵌入式硬件·面试·职场和发展
爷_5 小时前
字节跳动震撼开源Coze平台!手把手教你本地搭建AI智能体开发环境
前端·人工智能·后端
lemonth6 小时前
个人发展之路
面试
charlee447 小时前
行业思考:不是前端不行,是只会前端不行
前端·ai
Amodoro8 小时前
nuxt更改页面渲染的html,去除自定义属性、
前端·html·nuxt3·nuxt2·nuxtjs
Wcowin8 小时前
Mkdocs相关插件推荐(原创+合作)
前端·mkdocs
伍哥的传说9 小时前
CSS+JavaScript 禁用浏览器复制功能的几种方法
前端·javascript·css·vue.js·vue·css3·禁用浏览器复制
lichenyang4539 小时前
Axios封装以及添加拦截器
前端·javascript·react.js·typescript
Trust yourself2439 小时前
想把一个easyui的表格<th>改成下拉怎么做
前端·深度学习·easyui
三口吃掉你9 小时前
Web服务器(Tomcat、项目部署)
服务器·前端·tomcat