前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库Symbol.hasInstance 自定义 instanceof 行为
JavaScript对象与原型

Symbol.hasInstance 是怎么改写 instanceof 结果的?

x instanceof C 执行时,会先看 C 有没有 Symbol.hasInstance 方法,有就调用它,返回值转成布尔就是结果。在 class 里写一个 static [Symbol.hasInstance](value),就能完全接管 instanceof 的判断。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#对象与原型
先看核心答案读代码示例
理解线索

`instanceof` 的判断顺序

  1. Symbol.hasInstance右边对象上有这个方法,就用它的返回值作为结果
  2. Function.prototype所有函数默认继承的 Symbol.hasInstance,内部走原型链判断
  3. 原型链查找沿 x 的原型链找有没有等于 C.prototype 的那一个

Function.prototype[Symbol.hasInstance] 是不可写的,所以 Foo[Symbol.hasInstance] = fn 这种赋值在非严格模式下静默失败,严格模式下报错。要用 static 声明或 Object.defineProperty。

核心回答

先记住这个答案

x instanceof C 并不是直接查原型链。引擎先去 C 身上找 Symbol.hasInstance 方法,找到就调用 C[Symbol.hasInstance](x),返回值转成布尔就是结果。普通函数都从 Function.prototype 继承了一个默认实现,默认实现才去做原型链查找。所以只要在 class 里写 static [Symbol.hasInstance](value),就能让 instanceof 按你的规则判断。

  • hasInstance 可改写实例判断
  • 用静态符号方法定义匹配钩子
  • 改写后原型链判断被整个替换掉
  • 右边不是对象会抛 TypeError

用静态方法接管 instanceof

最直观的例子:定义一个 Even 类,让 instanceof Even 判断一个数是不是偶数。

让 instanceof 判断偶数JavaScript
class Even {
  // instanceof 右边是 Even 时,会调用这个静态方法
  static [Symbol.hasInstance](value) {
    return Number.isInteger(value) && value % 2 === 0
  }
}

console.log(2 instanceof Even)          // 原始值也能判断了
console.log(3 instanceof Even)
console.log(new Even() instanceof Even) // 原型链判断被整个替换掉了
查看输出与解释
true
false
false

第一行说明原始值也能参与判断。正常情况下 2 instanceof Number 是 false,因为原始值没有原型链可查,但自定义的方法想怎么判断都行。最后一行容易被忽略:new Even() 明明是 Even 的实例,结果却是 false。因为方法里只判断了数字,原来的原型链逻辑已经不会执行了。

实际能拿它做什么

真实项目里很少为了炫技改 instanceof,常见的是下面两种需求。

鸭子类型判断
「鸭子类型」是说不管对象是谁创建的,只要长得像就当它是。比如定义一个 Iterable 类,static [Symbol.hasInstance](obj) 里判断 obj != null && typeof obj[Symbol.iterator] === 'function'。之后 [] instanceof Iterable、new Map() instanceof Iterable、字符串对象都能返回 true,写类型守卫时读起来很自然。
兼容跨 iframe 或多份库的对象
instanceof 的原型链判断有个老问题:每个 iframe 有自己的一套全局对象,iframe 里创建的数组,它的原型是那个 iframe 的 Array.prototype,在主页面用 instanceof Array 判断就是 false。项目里打包进两份同一个库也会有类似情况。这时可以在自己的类上改写 Symbol.hasInstance,改成检查一个品牌标记,比如对象上有没有某个特定 Symbol 属性,不再依赖原型对象是不是同一个。数组本身直接用 Array.isArray 就行,它不受 iframe 影响。
回答前,多想一步

容易答错的地方

直接给函数赋值 Symbol.hasInstance
写 Foo[Symbol.hasInstance] = () => true 不会生效。Foo 从 Function.prototype 继承的那个 Symbol.hasInstance 是不可写的,按规则,继承来的属性不可写时,给子对象赋同名属性也会失败。非严格模式下静默忽略,严格模式下抛 TypeError。正确做法是 class 里用 static 声明,或者用 Object.defineProperty(Foo, Symbol.hasInstance, { value: fn })。
以为方法定义在实例原型上也有用
instanceof 只查右边那个对象,也就是构造函数本身。把 [Symbol.hasInstance] 写成实例方法,它在 C.prototype 上,instanceof 不会去看,所以一定要写 static。
以为右边必须是函数
有了 Symbol.hasInstance 之后,右边只要是对象就行。x instanceof { [Symbol.hasInstance]: v => true } 合法,返回 true。但右边既不是函数、又没有这个方法时会抛 TypeError。
试着用自己的话回答

面试官还会怎么问?

手写 instanceof 时要不要考虑 Symbol.hasInstance?

面试手写通常只写原型链那部分:用 Object.getPrototypeOf 从左边对象一层层往上找,遇到等于右边 prototype 的就返回 true,找到 null 返回 false。能补一句「完整实现要先检查右边的 Symbol.hasInstance」,说明你知道规范里的真实顺序。

除了 Symbol.hasInstance,还有哪些 well-known Symbol 能改语言行为?

常见的有 Symbol.iterator 控制 for...of 和展开运算符,Symbol.toPrimitive 控制对象转原始值,Symbol.toStringTag 控制 Object.prototype.toString 输出的类型标签。它们都是给对象开的「钩子」,让语言内置操作调用你定义的方法。

改写 instanceof 有什么风险?

它会让一个看起来很基础的运算符变得不可预测,读代码的人很难想到 instanceof 被改过。TypeScript 的类型收窄也只看类的声明,不知道你的自定义规则,可能推出错误的类型。所以除非是库内部需要统一判断,一般直接写一个 isXxx 函数更清楚。

从一道题,走向一组知识

把知识连起来

对象与原型

isPrototypeOf 和 instanceof 都查原型链,区别在哪?

isPrototypeOf 是另一种原型链判断,它不看 Symbol.hasInstance,不会被这种改写影响。

对象与原型

Object.keys、getOwnPropertyNames、getOwnPropertySymbols 各能拿到哪些键?

Symbol.hasInstance 是 Symbol 键,Object.keys 列不出来,要用 Object.getOwnPropertySymbols 才能看到。

参考资料

  • JavaScript:对象与原型参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 用静态方法接管 instanceof
  3. 实际能拿它做什么
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑