先记住这个答案
typeof 对原始类型(除 null)返回正确类型,但对对象统一返回 'object',无法区分数组、函数等,且 null 返回 'object' 是历史 bug。instanceof 检查构造函数的 prototype 是否在对象原型链上,适用于对象,但对原始类型返回 false,对跨 realm 对象会失效,且可被修改原型链或 Symbol.hasInstance 干扰。
- typeof 适合基本类型除 null
- instanceof 只查对象原型链
- 跨 realm 用 Array.isArray 等
typeof 和 instanceof 各自的底层机制
typeof 是运算符,在 JS 引擎内部根据值的内部类型返回固定字符串。对原始类型返回准确类型,如 typeof 42 === 'number'、typeof 'x' === 'string'、typeof true === 'boolean'、typeof undefined === 'undefined'、typeof Symbol() === 'symbol'、typeof 10n === 'bigint'。但 null 返回 'object',这是早期实现遗留的 bug,因 null 的二进制标记全零被识别为对象。对于所有对象(函数除外)统一返回 'object',函数返回 'function'。因此 typeof 无法区分数组、日期、正则等。
instanceof 是检查右侧函数的 prototype 属性是否出现在左侧对象的原型链中,返回布尔值。它要求左侧必须是对象,原始类型直接返回 false,如 'abc' instanceof String 为 false。它基于原型链查找,所以可以判断继承关系。instanceof 的行为可通过 Symbol.hasInstance 自定义。它依赖构造函数的 prototype 属性,如果构造函数 prototype 被重新赋值,会影响结果。
一个跨 realm 场景:跨 iframe 类型判断
一个页面中通过 iframe 嵌入子应用,主脚本拿到一个 iframe.contentWindow 中的数组,如 const arr = iframe.contentWindow.array。此时 typeof arr === 'object',不能判断是数组。执行 arr instanceof iframe.contentWindow.Array 会返回 true,但只能用该 realm 的 Array;如果用主 realm 的 Array 判断则返回 false,因为不同的 iframe 拥有独立的全局环境,Array.prototype 不是同一个。
处理:若判断这个值是不是数组,最好的方法是直接用 Array.isArray(arr),它内部不依赖原型链,能正确识别跨 realm 的数组。类似地,判断普通对象跨 realm 可用 Object.prototype.toString.call(arr) 返回 '[object Array]'。这些方法避免手动比对构造器。
instanceof 失效的条件与替代
instanceof 依赖构造函数的 prototype 属性。若构造函数 prototype 被重新赋值,已存在的实例可能不再被视为它的实例,例如 function C(){} const o = new C(); C.prototype = {}; o instanceof C 变为 false。同理,修改对象的 proto 或使用 Object.create 改变原型也会改变结果。
包装对象场景下,new String('x') instanceof String 为 true,但 'x' instanceof String 为 false,因为原始值没有原型链可查。typeof new String('x') 返回 'object'。所以判断原始字符串应使用 typeof,判断包装对象可使用 instanceof 或检查 constructor。在类继承中,instanceof 能反映原型链,但不保证实例具有类的私有字段,故不能完全依赖它做品牌校验。
容易答错的地方
- typeof 判断对象类型可靠
- 误以为 typeof 能区分数组、日期等。实际上 typeof [] 返回 'object',与普通对象无异。要区分应使用 Array.isArray 或 Object.prototype.toString。
- instanceof 不存在跨 realm 问题
- 忽视跨 realm 场景。当对象来自不同 iframe、window 或 worker 时,其原型与当前全局构造函数不同,instanceof 判断会失败。应使用能跨 realm 的 API 如 Array.isArray。
面试官还会怎么问?
typeof null 为何返回 'object'?现在能修复吗?
这是历史遗留,源自 JS 第一版中 null 的类型标签是 0,与对象相同。修复会造成大量现有代码崩坏,所以保留。可改用 obj === null 来判断。
Symbol.hasInstance 如何影响 instanceof?
构造器可定义静态方法 Symbol.hasInstance,该方法会在 instanceof 时被优先调用,返回布尔值覆盖默认原型链检查。常用于自定义品牌检查或模拟类型判断。
判断一个值是否是函数,typeof 和 instanceof 哪个好?
typeof 更稳妥,因为 typeof fn === 'function' 对普通函数和 class 都成立;instanceof 需与特定构造器比较且跨 realm 失效。若需判断是否为可调用对象,typeof 足够。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。