JavaScript面向对象的封装、继承、多态通过函数作用域、原型链、this绑定等机制模拟实现:封装靠闭包或#私有字段隐藏细节;继承推荐组合式(call+Object.create);多态依赖运行时原型链查找与方法重写。

JavaScript 中面向对象的三大特性——封装、继承、多态——不是靠语法强制保障的,而是通过语言机制(如函数作用域、原型链、this 绑定、类语法等)模拟实现的。它们的落地方式和传统静态语言(如 Java)不同,更灵活也更依赖开发者的设计意识。
封装:靠作用域和访问控制机制“藏住”内部细节
JS 没有原生 private 关键字(ES2022 才引入 # 私有字段,但兼容性有限),所以主流做法是:
- 用函数作用域包裹变量和方法,只暴露必要的接口(闭包)
- 用 ES6 class + # 前缀定义真正私有字段(例如 #name),仅限类内访问
- 约定俗成用下划线前缀(如 _age)表示“不建议外部访问”,但不阻止访问
- 把校验逻辑写在 setter 或公共方法里,而不是放任属性被直接赋值
比如创建一个带年龄校验的 Person:
function Person(name, age) {let _name = name;
let _age = age > 0 ? age : 0;
this.getName = () => _name;
this.setAge = (val) => { if (val > 0) _age = val; };
this.getAge = () => _age;
}
继承:核心是复用父类逻辑,主流用组合式(构造函数 + 原型链)
JS 不支持类间直接 extends 的“真继承”,但可通过多种方式达成效果。最实用、推荐的是组合式继承:
- 用 Parent.call(this, ...) 继承实例属性(保证每个子实例有独立副本)
- 用 Child.prototype = Object.create(Parent.prototype) 继承原型方法(共享方法,节省内存)
- 再补上 Child.prototype.constructor = Child 修正构造器指向
- ES6 class 语法(class Child extends Parent)底层就是这套逻辑的语法糖,可直接用,但需理解其行为
注意:单纯用 Child.prototype = new Parent()(原型链继承)会导致所有子实例共享父类引用类型属性,容易出错。
多态:靠方法重写 + 运行时动态绑定,关键在“同名不同实现”
JS 天然支持多态,因为方法调用是运行时查原型链决定的。只要子类覆盖了父类同名方法,调用时就会执行子类版本:
- 子类实例调用某个方法时,引擎从自身开始找,找不到就沿原型链向上找
- 如果父类和子类都有 render(),子类实例调用的就是子类的 render()
- 配合 instanceof 或 constructor 判断类型,可以实现条件分支下的不同行为
- 函数参数接受任意对象,只要它有需要的方法(鸭子类型),就是多态的自然体现
例如 Animal 和 Dog 都有 makeSound(),但具体输出不同,传给统一的 play(animal) 函数就能自动适配。



















