面试官问"JS的类型"时,到底想听到什么?

面试官问"JS的类型"时,到底想听到什么?

这不是一篇基础教程,而是一次思维升级。 如果你只停留在"知道有哪些类型"的层面,那你和面试官之间还差一个V8。


引子:一个最常见的面试场景

面试官问:"JavaScript 有哪些数据类型?"

初级回答(大部分人):

"有 string、number、boolean、null、undefined、object......还有 symbol?"

面试官(内心):背八股呢?

进阶回答(你):

"JS 的类型可以分为两大类------基本类型和引用类型。这种分类不是拍脑袋定的,而是由 V8 引擎的内存管理机制决定的。基本类型体积小,直接存在栈里;引用类型体积不定,存在堆里,栈里只存 8 字节的地址指针。这样做的好处是------"

面试官(眼睛亮了):这人懂底层。

差距在哪里? 前者只是列举 ,后者是解释为什么


一、先搞定分类------别让 null 坑了你

1.1 两张大表记清楚

基本类型(7种)------值直接存栈里

类型 例子 常见坑
string 'hello' ---
number 42 NaN 也是 number 类型
boolean true !! 是布尔转换的快捷方式
undefined let a 声明未赋值
null null ⚠️ typeof null'object'
bigint 10n ES2020 才加入
Symbol Symbol('id') 每次调用都是唯一值

引用类型(常见4种)------值存堆里,栈存地址

类型 例子 说明
Object { name: 'Alice' } 一切引用类型的祖宗
Array [1, 2, 3] typeof []'object'
Function function() {} JS 中函数也是对象
Date new Date() ⚠️ Date() 不是 Date 类型,是字符串!

1.2 终极记忆法

七个基本用一个口诀记住:

"宋(string)数(number)不(boolean)定(undefined)空(null)大(bigint)象(Symbol)"

七种基本类型,一个不少。
引用类型只记一句话:

"除了以上七种,其他全是对象"


二、核心区别------赋值行为见真章

自己跑一下这三行代码,你就懂了:

javascript 复制代码
// 场景 A:基本类型赋值
let a = 10;
let b = a;
b = 20;
console.log(a); // 猜猜是 10 还是 20?
javascript 复制代码
// 场景 B:引用类型赋值
let objA = { count: 10 };
let objB = objA;
objB.count = 20;
console.log(objA.count); // 猜猜是 10 还是 20?

答案:

  • 场景 A:10 ✅ --- 基本类型赋值是复制值,a 和 b 没关系了
  • 场景 B:20 ⚠️ --- 引用类型赋值是复制地址,objA 和 objB 指向同一个对象

记法:基本类型像复印件------改一个不影响另一个;引用类型像快捷方式------不管从哪个入口进去,改的都是同一个文件。


三、为什么 JS 要这么设计?------V8 的智慧

这是很多人知道"是什么"却说不清"为什么"的关键。

3.1 来,在脑子里画张图

ini 复制代码
执行这段代码时,V8 在内存里干了什么:

let name = 'GGBond';
let age = 20;
let girlFriend = {
  name: 'feifei',
  hobbies: ['coding', 'reading']
};

画出来是这样的:

css 复制代码
┌────────── 栈(Call Stack)──────────┐
│                                      │
│  name: 'GGBond'      ← 字符串直接存   │
│  age: 20            ← 数字直接存     │
│  girlFriend: ● ------------------------┐                │
│                     │                │
└─────────────────────│────────────────┘
                      │  引用地址(8 字节)
                      ▼
┌────────── 堆(Heap)────────────────┐
│                                      │
│  { name: 'feifei',                      │
│    hobbies: ● ------------→ ['coding',       │
│  }                        'reading'] │
│                                      │
└──────────────────────────────────────┘

3.2 为什么不能反过来?------V8 的真正考量

❌ 错误方案:所有数据都放栈里

如果 girlFriend 这个大对象也放进栈:

  • 栈的大小是固定的(通常 1-2MB)
  • 一个大对象可能就占几百 KB
  • 多来几个对象 → 爆栈(Stack Overflow)

❌ 错误方案:所有数据都放堆里

如果连 age = 20 也要去堆里分配:

  • 堆的访问速度比栈慢 10 倍以上
  • 一个数字才 8 字节,为了它去堆里开辟空间,得不偿失

✅ V8 的选择:混合存储

复制代码
基本类型(小、轻) → 栈    --- 快就完了
引用类型(大、不定)→ 堆    --- 栈不会爆
引用地址(8字节)  → 栈    --- 查询时通过地址找数据

一句话总结:栈管速度,堆管容量。V8 用最小代价换来了最大效率。


四、类型检测------避开常见的坑

4.1 三种检测方法

typeof ------ 适合基本类型(除 null)

javascript 复制代码
typeof 'hello'    // 'string'
typeof 42         // 'number'
typeof true       // 'boolean'
typeof undefined  // 'undefined'
typeof Symbol()   // 'symbol'
typeof 10n        // 'bigint'

typeof null       // 'object'  ← 历史遗留bug
typeof []         // 'object'  ← 数组也是object
typeof {}         // 'object'

规律:除了 null,基本类型的 typeof 都能对上。

instanceof ------ 适合引用类型

javascript 复制代码
[] instanceof Array        // true
{} instanceof Object       // true
new Date() instanceof Date // true

'hello' instanceof String  // false ❌ 基本类型不行

注意:[] instanceof Object 也是 true!因为原型链上能找到 Object。

Object.prototype.toString ------ 万能方法

javascript 复制代码
function getType(v) {
  return Object.prototype.toString.call(v).slice(8, -1);
}

getType('hello')    // 'String'
getType(null)       // 'Null'
getType([])         // 'Array'
getType(new Date()) // 'Date'
getType(new Map())  // 'Map'

4.2 面试常问:手写一个类型判断函数

javascript 复制代码
function typeOf(value) {
  // null 单独处理
  if (value === null) return 'null';
  
  // 基本类型用 typeof
  const base = typeof value;
  if (base !== 'object' && base !== 'function') return base;
  
  // 引用类型用 toString
  const tag = Object.prototype.toString.call(value);
  return tag.slice(8, -1).toLowerCase();
}

// 测试
typeOf('hello')   // 'string'
typeOf(null)      // 'null'     ✅ 修复了 typeof null 的 bug
typeOf([])        // 'array'
typeOf(new Date())// 'date'
typeOf(new Map()) // 'map'

五、V8 执行全过程------把知识串起来

5.1 完整流程

javascript 复制代码
let num = 10;
let str = 'hello';
let obj = { key: 'value' };

function add(x, y) {
  return x + y;
}

let result = add(num, 20);

V8 的执行步骤:

less 复制代码
Step 1:创建调用栈(Call Stack)
Step 2:创建全局执行上下文,压入栈底
Step 3:执行声明(创建阶段)
   ├── num → 栈中存 10
   ├── str → 栈中存 'hello'
   ├── obj → 堆中创建 {key:'value'},栈存地址 @001
   ├── add → 堆中存函数体,栈存地址 @002
   └── result → 栈中存 undefined
Step 4:执行 add(num, 20)
   ├── 创建函数执行上下文,入栈
   ├── x=10, y=20 存入栈中(函数的栈)
   ├── 执行 x + y → 返回 30
   └── 函数执行上下文出栈
Step 5:result = 30

5.2 这段代码会爆栈吗?

javascript 复制代码
function infinite() {
  return infinite();
}
infinite();

会。 每调一次就创建一个执行上下文入栈,栈是有限的,最终:

scss 复制代码
┌───────── 调用栈 ─────────┐
│ infinite()  // 第10000次  │
│ infinite()  // 第9999次   │
│ ...                       │
│ infinite()  // 第1次      │
│ 全局执行上下文             │
└──────────────────────────┘
RangeError: Maximum call stack size exceeded

这就是"爆栈"------引用类型放堆里而不是栈里,正是为了防止这种溢出。


写在最后

这篇文章没有讲什么高深的东西,而是用"为什么"把你知道的碎片知识串了起来

JS 类型的核心其实就三句话:

  1. 基本类型存值(栈),引用类型存地址(栈→堆)
  2. 这样设计是为了平衡速度(栈)和容量(堆)
  3. typeof 看基本,toString 看所有

能说出这三句,面试官就知道你不仅学过,还思考过


觉得有用的话点赞 👍 收藏 ⭐,让更多人看到。 你的面试路上还遇到过什么离谱的 JS 类型题?评论区告诉我。

相关推荐
gjwjuejin1 小时前
全埋点技术方案深度剖析:从事件代理到无痕采集的完整实现
javascript
前端若水2 小时前
在 Vue 2 与 Vue 3 中使用 markdown-it-vue 渲染 Markdown 和数学公式
前端·javascript·vue.js
之歆2 小时前
DAY_10 JavaScript 深度解析:原型链 · 引用类型 · 内置对象 · 数组方法全攻略(下)
开发语言·前端·javascript·ecmascript
__log3 小时前
ComfyUI 集成技术方案分析报告
javascript·python·django
ZC跨境爬虫3 小时前
跟着 MDN 学 HTML day_56:(HTML 表格基础完全指南)
前端·javascript·ui·html·音视频
江晓曼*凡云基地3 小时前
Hermes Agent 多Agent模式:并行拆解复杂任务的实战指南
javascript·windows·microsoft
小白学大数据4 小时前
Python 爬虫动态 JS 渲染与无头浏览器实战选型指南
开发语言·javascript·爬虫·python
飘尘4 小时前
WebAssembly 是什么?它为什么重要?
前端·javascript
之歆4 小时前
DAY_10 JavaScript 深度解析:原型链 · 引用类型 · 内置对象 · 数组方法全攻略(上)
开发语言·javascript·ecmascript