JavaScript基本类型与引用类型区别解析
作者:指尖跳动的光
文章介绍了JavaScript中基本类型和引用类型的区别,包括它们的存储位置、赋值行为、比较方式、可变性、类型数量、方法支持等特性,同时,还讨论了基本类型和引用类型在函数参数传递、深拷贝与浅拷贝、性能优化以及检测方法等方面的区别和注意事项,感兴趣的朋友一起看看吧
1. 基本类型(Primitive Types)
1.1 特点
- 按值访问:直接存储在栈内存中,变量保存的是数据的实际值。
- 不可变:修改基本类型的值会创建一个新值,原值不会被改变。
- 类型固定:JavaScript 的基本类型共有 7 种:
- number(数字)
- string(字符串)
- boolean(布尔值)
- null(空值)
- undefined(未定义)
- symbol(ES6 新增,唯一标识符)
- bigint(ES2020 新增,大整数)
1.2 示例
let a = 10; let b = a; // 赋值时复制实际值 b = 20; // 修改 b 不影响 a console.log(a); // 10 console.log(b); // 20
1.3 关键行为
- 赋值:复制实际值,两个变量独立。
- 比较:比较的是值是否相等。
let x = "hello"; let y = "hello"; console.log(x === y); // true(值相同)
- 方法调用:字符串、数字等基本类型调用方法时,会临时转换为对象(如 “abc”.toUpperCase()),但原值不变。
- 字符串调用方法
let str = "hello"; console.log(str.length); // 5 console.log(str.toUpperCase()); // "HELLO"
- str 是一个基本类型的字符串,但可以调用 length 属性和 toUpperCase() 方法。
- 引擎内部会临时将 str 转换为 new String(“hello”) 对象,调用方法后,临时对象被丢弃。
- 数字调用方法
let num = 3.14; console.log(num.toFixed(2)); // "3.14"
- num 是基本类型的数字,但可以调用 toFixed() 方法。
- 引擎临时将其转换为 new Number(3.14) 对象,调用方法后销毁。
- 布尔值调用方法
let bool = true; console.log(bool.toString()); // "true"
- bool 是基本类型的布尔值,但可以调用 toString() 方法。
- 引擎临时将其转换为 new Boolean(true) 对象,调用方法后销毁。
- 字符串调用方法
2. 引用类型(Reference Types)
2.1 特点
- 按引用访问:存储在堆内存中,变量保存的是数据的内存地址(引用)。
- 可变:修改引用类型的属性会直接影响原对象。
- 类型动态:包括所有对象类型:
- Object(普通对象)
- Array(数组)
- Function(函数)
- Date(日期)
- RegExp(正则表达式)
- Map、Set 等(ES6 新增)
2.2 示例
let obj1 = { name: "Alice" };
let obj2 = obj1; // 赋值时复制引用(地址)
obj2.name = "Bob"; // 修改 obj2 会影响 obj1
console.log(obj1.name); // "Bob"
console.log(obj2.name); // "Bob"
2.3 关键行为
- 赋值:复制引用(地址),两个变量指向同一对象。
- 比较:比较的是引用地址是否相同(即使内容相同,地址不同也返回 false)。
let arr1 = [1, 2]; let arr2 = [1, 2]; console.log(arr1 === arr2); // false(地址不同) console.log(arr1 === arr1); // true(同一对象)
- 方法调用:直接修改原对象(如 arr.push(3) 会改变原数组)。
3. 核心区别总结
| 特性 | 基本类型 | 引用类型 |
|---|---|---|
| 存储位置 | 栈内存(直接存储值) | 堆内存(存储地址,变量保存引用) |
| 赋值行为 | 复制实际值(独立) | 复制引用(共享同一对象) |
| 比较方式 | 比较值是否相等 | 比较引用地址是否相同 |
| 可变性 | 不可变(修改创建新值) | 可变(直接修改原对象) |
| 类型数量 | 固定 7 种 | 动态(所有对象类型) |
| 方法支持 | 临时转换为对象调用方法(原值不变) | 直接调用方法修改原对象 |
4. 常见场景与注意事项
4.1 函数参数传递
- 基本类型:函数内修改参数不影响外部变量。
function changeValue(num) { num = 100; } let a = 10; changeValue(a); console.log(a); // 10(未改变) - 引用类型:函数内修改对象属性会影响外部变量。
function changeObj(obj) { obj.name = "Bob"; } let person = { name: "Alice" }; changeObj(person); console.log(person.name); // "Bob"(被修改)
4.2 深拷贝 vs 浅拷贝
- 浅拷贝:仅复制引用(如 let arr2 = arr1),修改嵌套对象会互相影响。
- 深拷贝:完全复制对象(如使用 JSON.parse(JSON.stringify(obj)) 或工具库如 Lodash 的 _.cloneDeep)。
4.3 性能优化
- 基本类型(如频繁操作的数字、字符串)适合存储在栈中,访问速度快。
- 引用类型(如大型对象、数组)适合堆存储,但需注意避免意外共享引用。
5. 检测方法
5.1 基本数据类型-》检测类型
- typeof 运算符
- 适用于大多数基本类型,但 null 会返回 “object”(历史遗留问题)。
typeof "hello"; // "string" typeof 42; // "number" typeof true; // "boolean" typeof undefined; // "undefined" typeof Symbol("foo"); // "symbol" typeof 10n; // "bigint" typeof null; // "object" ⚠️ 特殊情况
- 适用于大多数基本类型,但 null 会返回 “object”(历史遗留问题)。
- 严格等于 === 检测 null 和 undefined
let x = null; let y = undefined; console.log(x === null); // true console.log(y === undefined); // true
- Object.prototype.toString.call()(最准确)
Object.prototype.toString.call("hello"); // "[object String]" Object.prototype.toString.call(42); // "[object Number]" Object.prototype.toString.call(true); // "[object Boolean]" Object.prototype.toString.call(null); // "[object Null]" Object.prototype.toString.call(undefined); // "[object Undefined]" Object.prototype.toString.call(Symbol("foo")); // "[object Symbol]" Object.prototype.toString.call(10n); // "[object BigInt]"
5.2 引用数据类型-》检测类型
- typeof 运算符
- typeof 对引用类型通常返回 “object”(除了 function):
typeof {}; // "object" typeof []; // "object" typeof function() {}; // "function" ⚠️ 特殊情况 typeof new Date(); // "object" typeof /regex/; // "object"
- typeof 对引用类型通常返回 “object”(除了 function):
- instanceof 运算符
- 检测对象是否是某个构造函数的实例:
[] instanceof Array; // true {} instanceof Object; // true function() {} instanceof Function; // true new Date() instanceof Date; // true - 局限性:
- 不能检测基本类型(如 “str” instanceof String 返回 false)。
- 跨框架(iframe)时可能不准确(不同全局对象的构造函数不同)。
- 检测对象是否是某个构造函数的实例:
- constructor 属性
- 返回对象的构造函数:
"hello".constructor === String; // true (42).constructor === Number; // true [].constructor === Array; // true ({}).constructor === Object; // true - 局限性:
- 如果对象是 null 或 undefined,会报错(Cannot read property ‘constructor’ of null)。
- 可以被手动修改(obj.constructor = Foo),不可靠。
- 返回对象的构造函数:
- Object.prototype.toString.call()(最准确)
Object.prototype.toString.call([]); // "[object Array]" Object.prototype.toString.call({}); // "[object Object]" Object.prototype.toString.call(function() {}); // "[object Function]" Object.prototype.toString.call(new Date()); // "[object Date]" Object.prototype.toString.call(/regex/); // "[object RegExp]"
5.3 综合检测方法
- 判断是否为基本类型
function isPrimitive(value) { return ( value === null || value === undefined || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "symbol" || typeof value === "bigint" ); } - 判断具体类型(推荐 Object.prototype.toString)
function getType(value) { return Object.prototype.toString.call(value).slice(8, -1).toLowerCase(); } getType("hello"); // "string" getType(42); // "number" getType([]); // "array" getType({}); // "object" getType(null); // "null" getType(undefined); // "undefined" getType(function() {}); // "function" getType(new Date()); // "date" - 判断是否为数组
Array.isArray([]); // true [] instanceof Array; // true Object.prototype.toString.call([]) === "[object Array]"; // true
- 判断是否为 null 或 undefined
function isNullOrUndefined(value) { return value === null || value === undefined; }
5.4 检测类型总结
| 检测方式 | 基本类型 | 引用类型 | 特殊情况 |
|---|---|---|---|
| typeof | ✅(除 null) | ⚠️(function 除外) | null 返回 “object” |
| instanceof | ❌ | ✅ | 跨框架不准确 |
| constructor | ❌(除 new String() 等) | ✅ | 可被修改,null/undefined 报错 |
| Object.prototype.toString.call() | ✅ | ✅ | 最准确 |
- 推荐做法
- 基本类型:优先用 typeof + 特殊处理 null。
- 引用类型:用 Object.prototype.toString.call() 或 Array.isArray() 等特定方法。
- 避免 instanceof 和 constructor,除非明确知道它们的局限性。
到此这篇关于JavaScript基本类型与引用类型有什么区别的文章就介绍到这了,更多相关js 基本类型与引用类型区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
