返回首页

说说JavaScript中的数据类型?存储上的差别?

问题解析

在 JavaScript 中,数据类型可以分为两大类:基本类型(Primitive Types)和引用类型(Reference Types)。理解这两种类型的区别,特别是它们在内存中的存储方式,是掌握 JavaScript 核心概念的关键。

核心概念

基本类型(Primitive Types)

基本类型是值类型,在内存中直接存储值本身,共有 7 种:

类型 说明 示例
Number 数值类型(整数和浮点数) 42, 3.14
String 字符串 "hello"
Boolean 布尔值 true, false
Undefined 未定义 undefined
Null 空值 null
Symbol 符号(ES6新增) Symbol('desc')
BigInt 大整数(ES2020新增) 9007199254740991n

引用类型(Reference Types)

引用类型是对象类型,在内存中存储的是指向堆内存的引用地址:

  • Object - 普通对象
  • Array - 数组
  • Function - 函数
  • Date - 日期
  • RegExp - 正则表达式
  • Map / Set / WeakMap / WeakSet

详细解答

1. 基本类型的特点

// Number 类型
let intNum = 55;           // 十进制
let octNum = 070;          // 八进制(0开头)= 56
let hexNum = 0xA;          // 十六进制(0x开头)= 10
let floatNum = 3.125e7;    // 科学计数法 = 31250000

// 特殊值 NaN
console.log(0 / 0);        // NaN
console.log(-0 / +0);      // NaN

// Undefined 类型
let message;
console.log(message);      // undefined
console.log(typeof message); // "undefined"

// Null 类型
let car = null;
console.log(typeof car);   // "object"(历史遗留bug)
console.log(null == undefined); // true
console.log(null === undefined); // false

// String 类型
let str = "hello";
// 字符串不可变
str = str + " world";      // 实际上是创建新字符串

// Symbol 类型
let sym1 = Symbol('foo');
let sym2 = Symbol('foo');
console.log(sym1 === sym2); // false(每个Symbol都是唯一的)

2. 引用类型的特点

// Object
let person = {
    name: "Nicholas",
    age: 29
};

// Array - 可以存储任意类型,动态大小
let colors = ["red", 2, { age: 20 }];
colors.push("blue");

// Function - 实际上是 Function 类型的实例
function sum(a, b) {
    return a + b;
}
// 等价于
let sum2 = function(a, b) {
    return a + b;
};
// 箭头函数
let sum3 = (a, b) => a + b;

3. 存储区别详解

基本类型的存储

基本类型存储在**栈(Stack)**中,直接保存值:

let a = 10;
let b = a;  // 复制值,b 得到的是 10 的副本
b = 20;
console.log(a); // 10(a 不受影响)

内存示意:

栈内存
┌─────────┐
   a: 10 
   b: 20    独立的存储空间
└─────────┘

引用类型的存储

引用类型在堆(Heap)中存储对象,在中存储引用地址:

let obj1 = { name: "Xxx" };
let obj2 = obj1;  // 复制引用地址,指向同一个对象
obj2.name = "Yyy";
console.log(obj1.name); // "Yyy"(obj1 也被修改了)

内存示意:

栈内存              堆内存
┌──────────┐       ┌─────────────────┐
│ obj1: ───┼──────→│ { name: "Yyy" } │
│ obj2: ───┘       └─────────────────┘
└──────────┘       (两个变量指向同一对象)

深入理解

1. 类型检测方法对比

// typeof - 适合检测基本类型
console.log(typeof 42);           // "number"
console.log(typeof "hello");      // "string"
console.log(typeof undefined);    // "undefined"
console.log(typeof null);         // "object"(bug)
console.log(typeof []);           // "object"
console.log(typeof function(){}); // "function"

// instanceof - 适合检测引用类型
console.log([] instanceof Array);           // true
console.log({} instanceof Object);          // true
console.log(function(){} instanceof Function); // true

// Object.prototype.toString - 最准确的方法
console.log(Object.prototype.toString.call([]));      // "[object Array]"
console.log(Object.prototype.toString.call(null));    // "[object Null]"
console.log(Object.prototype.toString.call(123));     // "[object Number]"

2. 赋值与传参的本质

// 基本类型传值
function changeValue(x) {
    x = 100;
}
let num = 10;
changeValue(num);
console.log(num); // 10(不变)

// 引用类型传引用
function changeProperty(obj) {
    obj.name = "changed";
}
let person = { name: "original" };
changeProperty(person);
console.log(person.name); // "changed"(被修改了)

// 但重新赋值不会影响原对象
function replaceObject(obj) {
    obj = { name: "new" };  // 只是改变了局部变量的指向
}
replaceObject(person);
console.log(person.name); // "changed"(不变)

3. 深拷贝与浅拷贝

// 浅拷贝 - 只复制一层
let obj = { a: 1, b: { c: 2 } };
let shallow = { ...obj };  // 或 Object.assign({}, obj)
shallow.b.c = 3;
console.log(obj.b.c); // 3(原对象也被修改)

// 深拷贝 - 递归复制所有层级
let deep = JSON.parse(JSON.stringify(obj));
deep.b.c = 4;
console.log(obj.b.c); // 3(原对象不受影响)

最佳实践

  1. 使用严格相等(===)进行比较,避免类型转换带来的意外结果
  2. 判断 null 时直接使用 === null,不要使用 typeof
  3. 处理引用类型时注意共享引用的问题,需要独立副本时使用深拷贝
  4. 使用 const 声明不会重新赋值的变量,用 let 声明会改变的变量
  5. 避免意外创建全局变量,使用严格模式 "use strict"
// 推荐:判断 null
if (value === null) { }

// 推荐:深拷贝简单对象
const deepClone = JSON.parse(JSON.stringify(obj));

// 推荐:使用 const 和 let
const PI = 3.14159;
let count = 0;

面试要点

  1. JavaScript 有哪些数据类型?

    • 基本类型:Number、String、Boolean、Undefined、Null、Symbol、BigInt
    • 引用类型:Object(包括 Array、Function 等)
  2. 基本类型和引用类型的区别?

    • 存储位置:基本类型在栈中,引用类型对象在堆中
    • 赋值行为:基本类型复制值,引用类型复制引用地址
    • 比较行为:基本类型比较值,引用类型比较引用地址
  3. 为什么 typeof null 返回 "object"?

    • 这是 JavaScript 的历史遗留 bug,null 实际上是基本类型
  4. 如何判断一个变量是否为数组?

    • Array.isArray(arr)
    • arr instanceof Array
    • Object.prototype.toString.call(arr) === "[object Array]"