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,所以原对象没有被改变。


总结:

  • 在函数内部修改对象的属性,会影响到原对象,因为通过相同的引用地址操作堆中的同一个对象。
  • 在函数内部重新赋值参数(即改变参数变量的指向),不会影响外部变量,因为传递的只是外部变量所存储的地址值的副本,函数内部改变的是这个副本(指向了一个新对象),而外部变量仍然指向原对象。
相关推荐
用户676081149115 分钟前
个人图书室
面试
愿得一猪蹄7 分钟前
浏览器开发者工具隐藏技巧,你可能只用了一成
前端
柒和远方9 分钟前
企业级多模态知识库:从文件入库、跨模态检索到可追溯回答
面试
高晶18 分钟前
ARM 嵌入式 Linux 内核编译与结构解析实战
前端·架构
健康活着就好23 分钟前
# 手写Promise.all和Promise.race,面试再也不怕了
面试·json
今年下半年1 小时前
【前端】Vue2 项目 ECharts 统计图表组件封装与使用实践(附组件源码)
前端·javascript·echarts
八月的雨季 最後的冰吻1 小时前
C++ 面试要点笔记(续)
c++·笔记·面试
西索斯coding1 小时前
在 中接入 教程:p配置、m填写、扩展思考参数一篇就够(2)
java·前端·javascript·ai
代码猎人1 小时前
前端怎么优化项目基础架构
前端
__sjfzllv___1 小时前
在职前端Leader学习/转行 AI Agent -DAY78
前端