先记住这个答案
typeof 用在原始类型上很好用,一个运算符就能区分 number、string、boolean、undefined、symbol、bigint 和 function。但它有两个盲区:typeof null 返回 "object",所有普通对象(数组、日期、正则)也都返回 "object"。instanceof 弥补了后一个问题,它沿原型链往上找,能判断一个对象是不是某个构造函数的实例。但 instanceof 对原始值不起作用,"hello" instanceof String 是 false。
typeof null误报为objecttypeof区分不了数组、日期等对象类型instanceof对原始值返回false- 跨 iframe 判数组用
Array.isArray
typeof 的返回值和盲区
typeof 对所有原始类型都能给出准确的类型字符串,唯一的例外是 null——这是 JavaScript 第一版实现时遗留的 bug。对象方面,只有函数会返回 "function",其他对象一律返回 "object",没法进一步区分。
typeof 结果JavaScriptconsole.log(typeof 42);
console.log(typeof 'hello');
console.log(typeof true);
console.log(typeof undefined);
console.log(typeof null);
console.log(typeof [1, 2, 3]);
console.log(typeof { a: 1 });
console.log(typeof function(){});查看输出与解释
number
string
boolean
undefined
object
object
object
function数组和普通对象的 typeof 结果一样,都是 "object"。如果你想知道一个值是不是数组,typeof 帮不了你。typeof null 返回 "object" 更是经典面试题,这不代表 null 是对象,只是历史实现的 bug 一直没修。
typeof 还有一个特殊能力:对未声明的变量不会报错,而是返回 "undefined"。这在检查全局变量是否存在时偶尔有用,比如 typeof jQuery !== "undefined"。
instanceof 怎么沿原型链查找
instanceof 的判断逻辑是:拿右侧构造函数的 prototype 属性,在左侧对象的原型链上往上找,找到就返回 true。所以 [] instanceof Array 是 true,因为数组实例的原型链上有 Array.prototype。同样 [] instanceof Object 也是 true,因为原型链最终会到 Object.prototype。
instanceof 对原始值和对象的不同表现JavaScriptconsole.log([] instanceof Array);
console.log([] instanceof Object);
console.log('hello' instanceof String);
console.log(42 instanceof Number);
console.log(new Error('oops') instanceof Error);
console.log(/abc/ instanceof RegExp);查看输出与解释
true
true
false
false
true
true"hello" instanceof String 是 false,因为字符串字面量是原始值,不是 String 构造函数创建的对象实例,它的原型链上没有 String.prototype。只有 new String("hello") 才会返回 true,但实际项目中几乎没人用 new String。
这也是 instanceof 最大的局限:它只能用在对象上,对原始值一概返回 false。另一个问题是跨 iframe 场景——每个 iframe 有自己的全局环境和构造函数,父页面的 Array 和子 iframe 的 Array 不是同一个,所以跨 iframe 传过来的数组 instanceof Array 会返回 false。这就是 Array.isArray() 存在的原因,它不依赖原型链,跨 iframe 也能正确判断。
容易答错的地方
- 用
typeof判断数组 typeof []返回"object",和普通对象没有区别。判断数组应该用Array.isArray(),它在任何环境下都可靠,包括跨 iframe。- 认为
instanceof的结果不会变 instanceof查的是当前的原型链。如果用Object.setPrototypeOf()修改了对象的原型,或者手动改了构造函数的prototype属性,instanceof的结果就会跟着变。它反映的是运行时的原型关系,不是创建时的。
面试官还会怎么问?
有没有一种方法能可靠判断任意值的类型?
Object.prototype.toString.call(value) 返回 "[object Type]" 格式的字符串,对 null 返回 "[object Null]",对数组返回 "[object Array]",对日期返回 "[object Date]"。它不依赖原型链,是最通用的类型判断方式。
Symbol.hasInstance 是做什么的?
它可以自定义 instanceof 的行为。在类上定义 static [Symbol.hasInstance](value) 方法后,value instanceof MyClass 会调用这个方法而不是走默认的原型链查找。不过实际项目中很少用到。
typeof 对 let 声明的变量在暂时性死区里会怎样?
会抛 ReferenceError。typeof 对未声明的变量不报错,但对 let 和 const 声明的、还没到声明语句的变量会报错。这是暂时性死区的规则,typeof 也绕不过去。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。