最基础的类型检测工具——typeof, instanceof

typeofinstanceof 是 JavaScript 中两个最基础但也最容易让人困惑的类型检测工具。要深入理解它们的原理,我们需要从 JavaScript 的底层数据存储、类型系统和原型链机制入手。


typeof

typeof 是一个一元运算符,它的核心任务是返回一个代表操作数类型的字符串。其原理深入到 JavaScript 引擎是如何在底层存储和标识变量的。

1. 核心原理:底层类型标签(Type Tagging)

在 JavaScript 的早期实现中,值在引擎内部是由一个**类型标签(Type Tag)**和实际的数据值来表示的。这个类型标签存储在变量的机器码低位中,用于标识该值的类型。

  • 底层存储机制:JavaScript 引擎在存储变量时,会在内存中为变量分配空间,并用低位的1-3个比特位来存储其类型信息。
  • typeof 的工作方式 :当你对变量使用 typeof 操作符时,JavaScript 引擎并不会去回溯变量的创建过程,而是直接读取这个变量在内存中机器码低位的类型标签,然后将其映射为对应的类型字符串返回。

2. 机器码的类型映射

不同的类型对应着不同的低位标识。一个常见的类型标签映射如下:

  • 000 :对象(object)。
  • 010 :浮点数(number)。
  • 100 :字符串(string)。
  • 110 :布尔值(boolean)。
  • 1 :整数(number),整数类型标签是1,但会被归为 number 类型。
  • -2^30undefined

3. 著名的历史遗留问题:typeof null === 'object'

这是 JavaScript 中最著名的Bug之一,至今未被修复以保持兼容性。

  • 原因 :如前所述,对象的类型标签是 000。而**null 在底层表示的是空指针,在大多数实现中,空指针的机器码全是 0**。因此,当 typeof 读取 null 的类型标签时,发现是 000,就错误地将其判断为 object

4. 特例:函数的识别

虽然函数在底层也是对象(类型标签是 000),但 typeof function(){} 返回的是 'function'。这是因为 JavaScript 引擎内部对可调用对象做了特殊处理。当 typeof 操作符检测到一个对象内部实现了 [[Call]] 方法时,它会特殊处理并返回 "function"


instanceof

instanceof 是一个二元运算符,用于检测一个对象 的原型链上是否存在另一个构造函数prototype 对象。它的核心是原型链查找

1. 核心原理:原型链检查

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。其内部机制可以简单地用以下代码模拟:

javascript 复制代码
function myInstanceof(instanceObj, constructorFunc) {
    // 1. 获取实例对象的隐式原型(内部 [[Prototype]] 属性)
    let proto = Object.getPrototypeOf(instanceObj);

    // 2. 获取构造函数的显式原型
    let prototype = constructorFunc.prototype;

    // 3. 沿着原型链向上查找
    while (true) {
        // 4. 如果找到头(null)还没找到,返回 false
        if (proto === null) {
            return false;
        }
        // 5. 如果找到了匹配的原型,返回 true
        if (proto === prototype) {
            return true;
        }
        // 6. 继续向上一级查找
        proto = Object.getPrototypeOf(proto);
    }
}

2. 工作步骤详解

假设我们执行 obj instanceof Constructor,引擎会执行以下步骤:

  1. 获取隐式原型 :获取左侧对象 obj 的内部 [[Prototype]] 属性(在浏览器中可以通过非标准 __proto__ 或标准 Object.getPrototypeOf() 访问)。
  2. 获取显式原型 :获取右侧构造函数 Constructorprototype 属性。
  3. 循环比较 :将 obj 的隐式原型与 Constructor 的显式原型进行比较。
    • 如果相等,返回 true
    • 如果不相等,则将 obj 的隐式原型的隐式原型(即原型链的上一级)取出来,再次与 Constructor.prototype 比较。
    • 这个过程持续进行,直到原型链的末端(即 null)。如果一直没找到相等的对象,则返回 false

3. 重要特征

  • 跨窗口问题instanceof 依赖于原型链,因此它不能跨不同的全局执行环境 (例如,来自 iframe 的数组在父页面中使用 array instanceof parentWindow.Array 会返回 false),因为它们的原型链指向的是不同的 Array.prototype 对象。
  • 只能用于对象 :由于 instanceof 的机制是查找原型链,对于原始类型(string, number, boolean 等),它们不是对象,没有原型链,因此直接用 instanceof 检测原始类型会始终返回 false

总结对比

特征 typeof instanceof
本质原理 读取变量机器码低位的类型标签 遍历左侧对象的原型链 ,查找右侧构造函数的 prototype
返回值 字符串(如 "string", "object", "function" 布尔值(true / false
适用场景 检测原始类型 (除 null 外)和函数。 检测对象类型及其继承关系。
局限性 null 返回 "object";数组、日期等对象均返回 "object",无法细分。 不能跨窗口(iframe)使用;不能用于检测原始类型。
相关推荐
To_OC5 小时前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode
To_OC5 小时前
LC 208 实现 Trie 前缀树:曾被名字劝退,写完发现是送分题
javascript·算法·leetcode
天渺工作室5 小时前
实现一个adblock/adblock plus等浏览器广告拦截器检测插件
前端·javascript
阳光是sunny6 小时前
Vue 项目怎么做用户行为全链路监控?轻量插件方案详解
前端·面试·架构
ZhengEnCi6 小时前
Q04-Vite禁用CSS代码分割-解决生产环境样式加载顺序混乱问题
前端·vue.js·vite
九酒7 小时前
AI Agent 开发踩坑记:口播功能非得用 APP 原生实现吗?
前端·人工智能·agent
Jackson__7 小时前
做了一段时间的AI coding后,我终于搞清了 CLI 和 MCP 的区别
前端·agent·ai编程
IT_陈寒10 小时前
JavaScript项目实战经验分享
前端·人工智能·后端
用户479492835691510 小时前
6w star,GitHub 趋势第一的 Ponytail,这个agent插件到底在火什么
前端·后端
薛定喵的谔12 小时前
我开源了一个精致的 Next.js 博客模板:Skyplume
前端·前端框架·next.js