javascript多态本质是运行时沿__proto__链动态查找方法:实例→构造函数prototype→父级prototype,取最近同名实现;可通过继承重写、鸭子类型或es6 class实现。

原型方法中的多态行为,本质是靠运行时动态查找机制实现的:调用一个方法时,JavaScript 引擎会沿着 __proto__ 链从实例开始逐级向上搜索,直到找到第一个匹配的方法。只要不同对象(或其原型)提供了同名但逻辑不同的实现,就自然表现出“同一接口、不同行为”。
用继承+方法重写模拟传统多态
这是最结构清晰、类型意图明确的方式,适合有层级关系的场景(比如动物家族、UI组件体系):
- 定义父构造函数,并在其
prototype上挂载默认方法,例如Animal.prototype.speak = function() { console.log('makes a sound'); } - 子构造函数(如
Dog)通过Dog.prototype = Object.create(Animal.prototype)建立原型链,再手动修复constructor - 在子原型上定义同名方法(如
Dog.prototype.speak = function() { console.log('woof!'); }),覆盖父级实现 - 创建
new Dog()和new Cat()实例,都调用.speak(),输出各自结果
靠鸭子类型实现轻量多态
不依赖继承关系,只关注“有没有这个方法”,更灵活,适合工具函数、插件系统等场景:
- 写一个通用函数,比如
function execute(thing) { if (typeof thing.run === 'function') thing.run(); } - 传入任意对象,只要它有
run方法就行:{ run() { console.log('engine starts'); } }或{ run() { console.log('script runs'); } } - 函数不检查构造器或原型链,只做存在性判断,执行时行为由传入对象决定
注意原型链查找优先级
多态能否生效,取决于方法在链上的位置。引擎总是取“最近”的那个实现:
- 实例自身属性(
this.method = function(){})优先级最高 —— 但不推荐,会破坏共享和可维护性 - 接着是当前构造函数的
prototype上的方法 - 再往上是父级
prototype,依此类推 - 如果子类没重写,就会落到父类方法上——这不是失败,而是继承的默认行为
ES6 class 只是语法糖,底层仍是原型链
class Animal { speak() {} } 和 class Dog extends Animal { speak() {} } 看起来更现代,但 Babel 编译后依然是基于 Object.create() 和 __proto__ 操作。多态逻辑完全一致,只是写法更直观、语义更清晰。











