Appearance
原型与继承
来源:
js/基础/4原型.html
速查导航
- prototype 与 __proto__ — 函数属性 vs 对象属性
- constructor 与原型链 — 构造函数引用与查找
- Function.prototype 特殊性 — 所有构造函数的共同原型
- instanceof 与原型修改 — 实例化前后修改原型的区别
- 原型链继承 — 子原型 = 父实例
- 构造函数继承 — call 借用父构造函数
- 组合继承 — 原型链 + 构造函数
- 原型式继承 — Object.create 浅拷贝
- 寄生式继承 — 增强 Object.create 返回对象
- 寄生组合式继承 — 最优继承方案
prototype 与 __proto__
prototype是函数的属性,用于定义实例共享的原型对象__proto__是对象的属性,指向其构造函数的prototype- 两者在实例上相等:
user.__proto__ === User.prototype
js
constructor 与原型链
实例本身没有 constructor,会沿原型链查找到 User.prototype.constructor。
js
Function.prototype 特殊性
Function.prototype 是所有构造函数(Object/Array/String 等)的共同原型。
js
instanceof 与原型修改
instanceof 判断的是 a.__proto__ === A.prototype,修改时机不同结果不同。
js
原型链继承
子类的 prototype 指向父类实例。
缺点: 引用类型属性共享,无法向父构造函数传参。
js
构造函数继承
通过 call 借用父构造函数,每个实例独立。
缺点: 无法继承父类 prototype 上的方法。
js
组合继承
原型链继承 + 构造函数继承,融合两者优点。
js
原型式继承
基于 Object.create 的浅拷贝,适合对象间继承。
缺点: 引用类型属性共享。
js
寄生式继承
在原型式继承基础上增强返回对象,添加自定义方法。
缺点: 每次创建对象都会重新创建方法。
js
寄生组合式继承
Object.create(parent.prototype) 替代 new Parent(),避免组合继承中多调用一次父构造函数。
这是 JavaScript 中最常用的继承模式。
js
ES6 class + extends
js
class Parent {
constructor(name) { this.name = name }
say() { console.log(this.name) }
}
class Child extends Parent {
constructor(name, age) {
super(name) // 必须先调用 super
this.age = age
}
}extends底层本质是寄生组合继承的语法糖super相当于Parent.call(this, name)- 子类
constructor中必须先调super()才能用this
ES5 vs ES6 关键区别
| 特性 | ES5 | ES6 |
|---|---|---|
| 写法 | 原型操作 | class 语法糖 |
| this 创建顺序 | 先创建子类 this,再拼接父类 | 先创建父类 this(super),再拼接子类 |
| 静态方法继承 | 需手动处理 | extends 自动继承静态方法 |
| 内置对象继承 | 无法继承 Array/Error 等 | extends 可继承内置对象 |
组合优于继承
- 继承的问题("大猩猩与香蕉"问题):无法选择性地继承属性,所有属性都会被继承
- 面向组合:将功能拆分为独立函数,按需组合
js
function drive() { console.log("wuwuwu!") }
function music() { console.log("lalala!") }
function addOil() { console.log("哦哟!") }
let car = compose(drive, music, addOil)
let newEnergyCar = compose(drive, music) // 按需组合,不需要的不装