JavaScript变量行为由类型(基本/引用)和作用域共同决定:基本类型栈中独占、赋值隔离;引用类型栈存地址、堆存实体、共享联动;作用域控制可见性与生命周期;参数传递均为值传递,但“值”含义不同。

JavaScript 中变量的行为,本质上由它的类型(基本类型 or 引用类型)和所在作用域共同决定。理解这两者的交互,才能真正避开赋值错乱、作用域污染、意外修改等常见坑。
基本类型:栈中独占,复制即隔离
Undefined、Null、Boolean、Number、String 这五种基本类型,值直接存储在栈内存中,空间固定、可直接访问。
- 声明时,变量名绑定的是一个具体的值,比如
let a = 42,a 就“持有”42; - 赋值给另一个变量(
let b = a),是把 42 完整拷贝一份到新栈位置,a 和 b 彼此独立; - 函数传参也是同理:参数接收的是值的副本,函数内改它,不影响外部原始变量。
引用类型:栈存地址,堆存实体,共享即联动
Object、Array、Function、Date、RegExp 等都属于引用类型。它们的“真身”存在堆内存里,而变量在栈中只保存一个指向该对象的地址(指针)。
-
let obj1 = { name: 'Alice' }:obj1 栈中存的是一个地址,指向堆中那个对象; -
let obj2 = obj1:复制的是这个地址,obj1 和 obj2 指向堆中同一个对象; - 所以
obj1.name = 'Bob'后,obj2.name也会变成 'Bob'; - 但若写
obj1 = { name: 'Charlie' },只是让 obj1 指向新对象,obj2 不受影响。
作用域如何影响这两类变量的生命周期与可见性
作用域管“谁能看到”,类型管“看到的是什么”。两者叠加,决定了变量是否可读、能否被修改、何时被回收。
- 全局作用域声明的变量(
var globalNum = 100或let globalObj = {}),整个脚本都可访问,生命周期直到页面卸载; - 函数作用域内用
let/const声明的基本类型,执行完函数就不可访问,引擎可立即回收栈空间; - 函数内创建的对象(如
const arr = [1,2,3]),即使函数执行结束,只要外部还有变量引用它(比如返回了 arr 或赋给了全局变量),堆中对象就不会被垃圾回收; -
var声明有变量提升,且在函数内声明仍属函数作用域;而let/const还引入块级作用域,if 或 for 块内声明的变量,出了块就不可见——这对基本类型和引用类型都生效,但对引用类型的“实体”无影响,只影响“谁还拿着它的地址”。
参数传递:全是值传递,但“值”的含义不同
JavaScript 没有引用传递。所谓“引用类型传进去能改外面”,是因为你传的是地址这个“值”,而地址指向的堆对象是共享的。
- 传基本类型:传的是数字、字符串本身,函数内改的是副本;
- 传引用类型:传的是地址(一个数值型指针),函数内通过这个地址操作堆对象,自然会影响外部;
- 关键区别在于:函数内能否让参数变量指向一个全新对象。可以(
obj = {}),则不影响外部;不可以(只改属性obj.x = 1),则外部同步变化。

















