JavaScript函数作用域内声明的let变量被外部函数修改问题咨询
问题原因解答
核心本质:和let声明无关,是JavaScript引用类型的参数传递特性导致的
1. 首先明确JavaScript的两类数据存储规则
- 基本类型(数值、字符串、布尔值、null、undefined、Symbol)存储在栈内存中,赋值、作为参数传递时,是直接拷贝完整的值,新变量和原变量完全独立,修改互不影响。
- 引用类型(数组、对象、函数)的实际内容存储在堆内存中,栈内存只存储指向堆内存的地址。当你把引用类型作为参数传给函数时,传递的是栈内存中地址的拷贝,而不是整个堆内存内容的拷贝,这就导致函数内的形参和函数外的实参,指向的是堆内存中完全相同的对象/数组。
2. 对应你的代码场景验证
你代码里的chartData是数组,属于引用类型,当你把它传给updateDatas、labelAndCategories两个函数时,两个函数里的形参chartData1拿到的是和preparDataChart内chartData相同的堆地址:
labelAndCategories里调用chartData1.push(),push是数组的原地修改方法,直接修改堆内存中的原数组updateDatas里调用chartData1.splice(),splice同样是原地修改数组的方法,修改的也是同一个堆内存里的数组
所以你会感觉到“在其他函数里修改了preparDataChart内的局部变量”,这是正常的语法特性,不是变量变成了全局变量。
3. 示例复现
你可以运行这段简单代码验证特性:
function modifyArr(arrParam) { arrParam.push('新增内容') // 原地修改数组 } function test() { let localArr = ['原有内容'] // 函数内局部变量 modifyArr(localArr) console.log(localArr) // 输出 ['原有内容', '新增内容'],局部变量被修改 } test()
4. 不想修改原数组的解决方法
如果需要避免修改原数组,传参时传入数组的拷贝即可:
- 如果是一维数组,用浅拷贝即可:
updateDatas(timeStartAndTimeEnd, [...chartData])或者updateDatas(timeStartAndTimeEnd, Array.from(chartData)) - 如果是多层嵌套的数组/对象,需要做深拷贝,比如用
JSON.parse(JSON.stringify(chartData))(注意不能处理函数、Symbol等特殊类型),或者自己实现深拷贝逻辑。
内容的提问来源于stack exchange,提问作者Janio Carvalho
相关产品推荐
相关产品推荐

