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


总结:

  • 在函数内部修改对象的属性,会影响到原对象,因为通过相同的引用地址操作堆中的同一个对象。
  • 在函数内部重新赋值参数(即改变参数变量的指向),不会影响外部变量,因为传递的只是外部变量所存储的地址值的副本,函数内部改变的是这个副本(指向了一个新对象),而外部变量仍然指向原对象。
相关推荐
恋猫de小郭44 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端