Skip to content

JavaScript 数据类型详解

提示

本文内容基于《JavaScript 高级程序设计(第 4 版)》及社区优质文章整理。如有理解偏差,请以官方规范或您的实践经验为准。

JavaScript 的数据类型体系主要包含两大类别:基本数据类型(Primitive Types)引用数据类型(Reference Types)

基本数据类型

注意:基本数据类型也被称为“原始数据类型”。

截至 ES2020 标准,JavaScript 共定义了 7 种基本数据类型:

  • undefined:表示未定义。
  • null:表示空指针或空值。
  • boolean:布尔值(true/false)。
  • string:字符串。
  • number:数值(包含整数和浮点数)。
  • symbol:独一无二的值(ES6 新增)。
  • bigint:任意精度的整数(ES2020 新增)。

核心特性

  • 存储方式:基本类型的值直接存储在**栈内存(Stack)**中。
  • 访问机制:采用按值访问(By Value),操作的是变量中实际存储的数据。
  • 不可变性:基本类型的值是不可变的,且不包含任何属性或方法。
  • 复制行为:赋值时会创建一个全新的副本,两者互不影响。

疑问:为何 'hello'.length 能正常工作?

既然基本类型没有属性和方法,为什么我们可以直接调用字符串的方法呢?

为了赋予基本类型操作能力,JavaScript 提供了 BooleanNumberString 三种基本包装类型。当读取基本类型的属性时,JS 引擎会在后台自动执行以下操作:

  1. 创建对应类型的包装对象实例(如 new String('hello'))。
  2. 调用该实例上的指定属性或方法。
  3. 操作完成后立即销毁该实例。
js
// 代码示例
const str = 'hello world'
const len = str.length // 访问 length 属性

/**
 * 引擎背后的实际操作流程:
 * 1. const temp = new String('hello world') // 创建临时包装对象
 * 2. const len = temp.length                // 读取属性
 * 3. temp = null                            // 销毁临时对象
 */

引用数据类型

除了上述基本类型外,其余所有类型均属于引用类型。常见的包括:

  • Object:普通对象
  • Array:数组
  • Function:函数
  • Date:日期对象
  • RegExp:正则表达式
  • Map / WeakMap:键值对集合(ES6)
  • Set / WeakSet:唯一值集合(ES6)

核心特性

  • 存储方式:引用类型的实际对象存储在堆内存(Heap)中,而变量在栈内存中仅保存指向该对象的内存地址(指针)
  • 访问机制:采用按引用访问(By Reference),操作的是对象的引用地址。
  • 复制行为:赋值时仅复制内存指针,两个变量指向同一个堆内存对象,修改其中一个会影响另一个。

栈内存 vs 堆内存

特性栈内存 (Stack)堆内存 (Heap)
存储内容基本类型值、引用类型指针引用类型实际对象
空间特点空间连续,系统自动分配释放空间不连续,由垃圾回收机制 (GC) 管理
存取速度较慢

扩展阅读:深入理解 JS 中的栈内存与堆内存

类型检测全解

JavaScript 中常用的类型检测手段主要有以下五种:

  1. typeof
  2. instanceof
  3. constructor
  4. Array.isArray()
  5. Object.prototype.toString

1. typeof

最基础的检测方法,适用于检测基本类型(null 除外)和函数。

js
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof 'feng feng' // 'string'
typeof 123 // 'number'
typeof Symbol() // 'symbol'
typeof BigInt(123) // 'bigint'

typeof null // 'object' (历史遗留 Bug)

关于 typeof null === 'object': 这是一个著名的历史遗留 Bug。在 JS 早期版本中,值的前 3 位用于表示类型,对象的标签是 000,而 null 被表示为全 0,因此被误判为 object

对于非函数的引用类型,typeof 统一返回 'object',无法区分具体类型。

js
typeof {} // 'object'
typeof [] // 'object'
typeof new Date() // 'object'
typeof console.log // 'function' (特殊)

2. instanceof

主要用于检测引用类型。其原理是检查构造函数的 prototype 属性是否出现在实例对象的原型链上。

js
// 基本类型检测无效
true instanceof Boolean // false

// 引用类型检测
const p1 = new Person('fengfeng')
p1 instanceof Person // true
p1 instanceof Object // true (原型链向上查找)

// 原型链修改会影响结果
Reflect.setPrototypeOf(p1, Array.prototype)
p1 instanceof Person // false
p1 instanceof Array // true

局限性

  • 无法检测基本类型。
  • 原型链可被修改,导致结果不准确。

3. constructor

通过访问实例的 constructor 属性来判断其构造函数。

js
;((123).constructor ===
  Number(
    // true
    [],
  ).constructor) ===
  Array // true

局限性

  • nullundefined 没有此属性。
  • 该属性可被随意修改,不可靠。

4. Array.isArray()

ES5 引入的专门用于检测数组的静态方法,比 instanceof 更稳健(不受 iframe 环境影响)。

js
Array.isArray([]) // true
Array.isArray({}) // false

5. Object.prototype.toString

这是最准确的类型检测方式。通过调用 Object.prototype.toString.call(value),可以返回格式为 [object Type] 的字符串,从而精准区分各种内置类型。

js
const toString = Object.prototype.toString

toString.call(null) // '[object Null]'
toString.call(undefined) // '[object Undefined]'
toString.call(123) // '[object Number]'
toString.call('abc') // '[object String]'
toString.call(true) // '[object Boolean]'
toString.call(Symbol()) // '[object Symbol]'

toString.call({}) // '[object Object]'
toString.call([]) // '[object Array]'
toString.call(new Date()) // '[object Date]'
toString.call(new RegExp()) // '[object RegExp]'
toString.call(new Map()) // '[object Map]'

原理简述: 当调用 toString 方法时,它会获取内部的 [[Class]] 属性(ES5)或 Symbol.toStringTag(ES6+),并组合成 [object Tag] 字符串返回。

扩展阅读:深入探究 Object.prototype.toString

如有转载或 CV 的请标注本站原文地址