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


总结:

  • 在函数内部修改对象的属性,会影响到原对象,因为通过相同的引用地址操作堆中的同一个对象。
  • 在函数内部重新赋值参数(即改变参数变量的指向),不会影响外部变量,因为传递的只是外部变量所存储的地址值的副本,函数内部改变的是这个副本(指向了一个新对象),而外部变量仍然指向原对象。
相关推荐
Highcharts.js2 小时前
缺失数据可视化图表开发实战|Highcharts创建人员出生统计面积图表示例
开发语言·前端·javascript·信息可视化·highcharts·图表开发
LaughingZhu9 小时前
Product Hunt 每日热榜 | 2026-05-21
前端·人工智能·经验分享·chatgpt·html
怕浪猫9 小时前
Electron 开发实战(一):从零入门核心基础与环境搭建
前端·electron·ai编程
Mahir089 小时前
Spring 循环依赖深度解密:从问题本质到三级缓存源码级解析
java·后端·spring·缓存·面试·循环依赖·三级缓存
小鹏linux10 小时前
Ubuntu 22.04 部署开源免费具有精美现代web页面的Casdoor账号管理系统
linux·前端·ubuntu·开源·堡垒机
前端若水11 小时前
会话管理:创建、切换、删除对话历史
前端·人工智能·python·react.js
绝知此事11 小时前
【算法突围 01】线性结构与哈希表:后端开发的收纳术
java·数据结构·算法·面试·jdk·散列表
Bigger11 小时前
mini-cc:一个轻量级 AI 编程助手的诞生
前端·ai编程·claude
涵涵(互关)11 小时前
Naive-ui树型选择器只显示根节点
前端·ui·vue
BY组态11 小时前
Ricon组态系统最佳实践:从零开始构建物联网监控平台
前端·物联网·iot·web组态·组态