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


总结:

  • 在函数内部修改对象的属性,会影响到原对象,因为通过相同的引用地址操作堆中的同一个对象。
  • 在函数内部重新赋值参数(即改变参数变量的指向),不会影响外部变量,因为传递的只是外部变量所存储的地址值的副本,函数内部改变的是这个副本(指向了一个新对象),而外部变量仍然指向原对象。
相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
晨米酱3 天前
AGENTS.md:Agent 的上下文策略层
面试·架构·agent
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
彧azz3 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
汉堡大王95273 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大3 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端