前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 热点
旧版
首页程序员面试题库mixin 模式 class 组合复用方法
JavaScript对象与原型

一个 class 只能继承一个父类,想复用多组方法怎么办?

mixin 就是把一组方法「混」进一个类,而不是通过继承得到。最常用的是类工厂写法:写一个函数接收父类、返回一个继承它的新类,多个这样的函数嵌套调用,就能叠加多组能力,super 和 instanceof 都照常可用。

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

两种 mixin 写法

  1. 类工厂函数接收父类返回子类,每个 mixin 在原型链上占一层,支持 super
  2. Object.assign把普通对象的方法复制到 prototype,简单但没有 super
  3. 组合不混进类,而是把能力对象作为属性持有,调用时转发

Object.assign 复制时会执行源对象的 getter,把当时的值作为普通属性写过去,getter 本身丢了。要保留 getter 得用 Object.getOwnPropertyDescriptors 配合 Object.defineProperties。

核心回答

先记住这个答案

JavaScript 的 class 只能 extends 一个父类。如果鸭子既要「能飞」又要「能游泳」,这两组方法没法都放在父类里,就用 mixin:把一组方法单独定义,再「混」进需要它的类。推荐的写法是类工厂,也就是一个接收父类、返回子类的函数,比如 const CanFly = (Base) => class extends Base { fly() {} },然后写 class Duck extends CanSwim(CanFly(Animal))。每个 mixin 在原型链上占一层,super 和 instanceof 都正常工作。

  • mixin 可向类叠加多组能力
  • 类工厂可逐层扩展基类
  • Object.assign 不复制不可枚举类方法
  • 同名方法后混入的覆盖先混入的

类工厂写法:一层层包起来

类工厂的思路是「继承链还是一条,只是中间多插了几层」。

给鸭子叠加两组能力JavaScript
const CanFly = (Base) => class extends Base {
  fly() { return `${this.name} 在飞` }
}
const CanSwim = (Base) => class extends Base {
  swim() { return `${this.name} 在游泳` }
}

class Animal {
  constructor(name) { this.name = name }
}

// 从里往外包:Animal → CanFly → CanSwim → Duck
class Duck extends CanSwim(CanFly(Animal)) {}

const duck = new Duck('唐老鸭')
console.log(duck.fly())
console.log(duck.swim())
console.log(duck instanceof Animal) // 链条还是完整的
查看输出与解释
唐老鸭 在飞
唐老鸭 在游泳
true

CanFly(Animal) 返回一个继承 Animal 的匿名类,CanSwim 再继承它,Duck 继承最外层。所以 duck 的原型链是 Duck.prototype → CanSwim 层 → CanFly 层 → Animal.prototype。方法都在各自那一层的原型上,所有实例共享,不会每个实例复制一份。 因为是真的继承,mixin 里可以写 super.method() 调用下一层的同名方法,多个 mixin 都重写 init 时就能串起来执行,这是另一种写法做不到的。

Object.assign 写法和它的坑

更早的写法是把方法写在普通对象里,然后 Object.assign(Duck.prototype, flyMixin, swimMixin)。它简单直接,Vue 2 的 mixins 选项、很多老库都是这个思路。

但它有几个限制。第一,方法是复制过去的,mixin 里没法用 super 调用父类方法,对象字面量方法里的 super 在复制之后仍然指向原来的对象。第二,只复制自身可枚举属性,getter 会被执行,写过去的是当时的值,getter 本身丢了。第三,同名方法直接覆盖,后面的 mixin 悄悄盖掉前面的,没有任何提示。

为什么 assign 一个 class 的原型什么都拿不到
有人会把 mixin 也写成 class,然后 Object.assign(Duck.prototype, CanFly.prototype),结果 duck.fly 是 undefined。原因是 class 里定义的方法都是不可枚举的,而 Object.assign 只复制可枚举属性。要么把 mixin 写成普通对象,要么用 Object.getOwnPropertyNames 遍历,再用 Object.defineProperty 逐个复制描述符。
回答前,多想一步

容易答错的地方

以为 mixin 就是多继承
JavaScript 里没有真正的多继承。类工厂只是把多个 mixin 排成一条线性链,顺序决定了同名方法谁覆盖谁:写在外层的 mixin 离子类更近,优先被找到。改变嵌套顺序,行为可能就变了。
mixin 用得越多越好
mixin 多了,一个方法到底来自哪一层很难追,名字也容易冲突。React 早期的 createClass 支持 mixins,后来官方明确不推荐,改用高阶组件,最后是 Hooks。能用组合解决的,比如把一个 logger 对象作为属性传进来,就不要用 mixin。
试着用自己的话回答

面试官还会怎么问?

两个 mixin 都定义了 init 怎么办?

类工厂写法里,让每个 mixin 的 init 先调用 super.init?.() 再做自己的事,调用就会沿着链一层层传下去,全部执行。Object.assign 写法没有这个能力,只能在混入前手动检测冲突,或者约定方法名加前缀。

TypeScript 里 mixin 怎么写类型?

官方文档给的写法是先定义构造函数类型 type Constructor = new (...args: any[]) => {},mixin 函数写成 function CanFly<TBase extends Constructor>(Base: TBase),返回 class extends Base。这样组合出来的类,TypeScript 能推断出所有混入的方法。

Vue 3 为什么不再推荐 mixins?

mixins 选项的问题是来源不清和命名冲突:模板里用到一个 loading,看不出它来自哪个 mixin,两个 mixin 定义同名数据还会合并或覆盖。Vue 3 推荐用组合式函数,const { loading } = useFetch() 返回值显式解构,来源一目了然,重名时自己改个名字就行。

从一道题,走向一组知识

把知识连起来

对象与原型

Object.create(null) 创建的对象有什么特别?哪些方法用不了?

类工厂 mixin 本质是在原型链上多插几层,Object.create 是手动指定原型的最底层写法。

对象与原型

属性设成 configurable:false 之后,哪些操作就做不了了?

想让混入的方法不可枚举、不被后面的 mixin 覆盖,要靠 defineProperty 设置属性描述符。

参考资料

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

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

本题目录
  1. 先记住这个答案
  2. 类工厂写法:一层层包起来
  3. Object.assign 写法和它的坑
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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