instanceof通过遍历对象的[[prototype]]链判断右侧构造函数的prototype是否在链中;它不依赖constructor属性,只基于不可篡改的内部原型链接,原始值恒返回false。

JavaScript 中的类型判断,尤其是对引用类型的识别,离不开原型链的遍历机制。它不是靠“记名字”完成的,而是通过对象内部的 [[Prototype]] 链一层层向上比对构造关系来实现的。
instanceof 的底层逻辑就是原型链遍历
当你写 f instanceof Foo 时,JavaScript 引擎实际在做这件事:
- 获取
f.__proto__ - 检查它是否等于
Foo.prototype - 如果不等,继续取
f.__proto__.__proto__,再比对 - 如此循环,直到找到匹配,或到达
Object.prototype.__proto === null
这个过程本质上是一次同步的、自下而上的原型链遍历。比如:
const arr = [];<br>console.log(arr instanceof Array); // true<br>console.log(arr instanceof Object); // true
因为 arr.__proto__ === Array.prototype,而 Array.prototype.__proto__ === Object.prototype,所以两次都命中。
constructor 属性不可靠,但原型链本身稳定
obj.constructor 看似能反映类型,但它容易被改写:
function Person() {}<br>const p = new Person();<br>p.constructor === Person; // true<br>p.constructor = Array;<br>p.constructor === Array; // true(但 p 并不是 Array 实例)
而原型链遍历不依赖 constructor,只依赖不可直接篡改的 [[Prototype]] 内部槽位(虽然 __proto__ 可设,但标准行为中构造函数与原型的绑定是初始化时确定的)。因此 instanceof 和 Object.getPrototypeOf() 遍历才是更可靠的类型线索。
Object.prototype.toString.call() 是更通用的类型快照
它绕过原型链遍历,直接读取内置对象的内部标签([[Tag]]):
-
Object.prototype.toString.call([])→"[object Array]" -
Object.prototype.toString.call(new Date())→"[object Date]" -
Object.prototype.toString.call(null)→"[object Null]"
这个方法不查找属性,也不走 __proto__,而是由引擎根据对象的内部类型标识直接返回字符串。它比 instanceof 更底层、更确定,适合需要精确区分原生类型(如 Array vs Arguments)的场景。
自定义类与原型链类型判断的注意事项
ES6 class 是语法糖,其原型链结构和传统构造函数一致:
class Animal {}<br>class Dog extends Animal {}<br>const dog = new Dog();<br>dog instanceof Dog; // true<br>dog instanceof Animal; // true<br>dog instanceof Object; // true
但要注意:如果用 Object.create(null) 创建对象,它没有 __proto__,原型链从一开始就是断的,所有 instanceof 判断都会返回 false,toString.call() 也会返回 "[object Object]" —— 因为它已脱离标准原型体系。











