一、JavaScript 原型
- 原型:
[[Prototype]]
- ECMA-262 把 原型链 定义为 ECMAScript 的主要继承方式
- 继承关系的判断:
sourceObject instanceof TargetObject
TargetObject.isInstanceOf(sourceObject)
- 原型设置:
Object.setPrototypeOf(target, source)
- 原型获取:
Object.getPrototypeOf(target)
- 原型判定:
target.isPrototypeOf(source)
- 所有引用类型的对象,其原型链顶端都是:
Object.prototype
二、JavaScript 继承
JavaScript 中的继承基本上都是基于原型的,主要有以下继承方式:
- 原型链继承
- 盗用构造函数继承
- 组合式继承
- 原型继承
- 寄生式继承
- 寄生组合式继承
1. 原型链继承
原型链继承是通过直接修改子构造函数的原型来实现的,示例代码如下:
function Super() {}
function Sub() {}
Sub.prototype = new Super();
const sub - new Sub();
console.log(sub instanceof Super); // true
这样的缺点有:
- 父类的实例属性成了子类的原型属性,会被子类所有实例共享
- 当实例属性是原始值时,是没有问题的
- 当实例属性是引用值时,会有巨大的问题:在一个地方发生了改动,会影响到所有的地方
- 必须在构造函数中定义方法,函数不能重用
2. 盗用构造函数继承
盗用构造函数继承,是通过在子类型中调用父类型的构造函数来实现的。
function Super(name) {
this.name = name
}
function Sub() {
Super.call(this, "Nico");
}
const sub = new Sub();
console.log(sub instanceof Super); // false
缺点:
- 必须在构造函数中定义方法,函数不能重用
- 子类不能访问父类原型上定义的方法
3. 组合继承
又名:伪经典继承。
组合了原型链 (继承方法) 和盗用构造函数 (继承属性) 继承。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age){ // 继承属性
SuperType.call(this, name); this.age = age;
}
// 继承方法
SubType.prototype = new SuperType();
// 子类自己的方法
SubType.prototype.sayAge = function() {
console.log(this.age);
};
- 优点:
- 保留了 instanceof 和 isPrototypeOf()识别合成对象的能力
- 缺点:
- 缺点:调用了两次 Super 的构造函数
4. 原型式继承
一种不涉及严格意义上的构造函数的继承方法。
- 适用场景:有一个对象,想在它的基础上再创建一个新对象。
- ES5 支持类似这样的创建方式:
const target = Object.create(source)
。Object.create
将原型式继承的概念规范化了
5. 寄生式继承
- 与原型式继承比较接近
- 思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种 方式增强对象,然后返回这个对象。
- 适用场景:主要关注对象,而不在乎类型和构造函数
- 缺点:
- 通过寄生式继承给对象添加函数会导致函数难以重用,与盗用构造函数继承模式类似
6. 寄生式组合继承
通过盗用构造函数继承属性,但使用混合式原型链继承方法。
基本模式:
function inheritPrototype(subType, superType) {
let prototype = object(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 赋值对象
}
这种继承方式可以算是引用类型继承的最佳模式。
三、JavaScript 中的类
- JavaScript 中的 class 和 extends 仅仅只是一个语法糖
- 本质仍然是基于原型链的继承
- 定义方式:
- 类声明 (类似函数声明)
- 类表达式 (类似函数表达式)
- 注意:
- 类产生的作用域实质是一个块级作用域
- 类中的 非 static 方法 的 this 始终指向类的实例
- 类的实例对象上不存在 static 方法
- 只能通过 ClassName.staticMethod() 的方式调用静态方法
- Class 最终仍继承自 Object
- 类中可以有的内容:
constructor
构造函数- 静态方法
- 实例方法
- getter 属性方法
- setter 属性方法
- 实例属性
- 类的继承
- 模式:
class SubType extends SuperType {}
- SuperType 的限制:
- 必须有 [[Construct]] 和 [[Prototype]]
- 在 SubType 中使用 super 的限制:
- 在类构造函数中,不能在调用 super()之前引用 this
- 如果在派生类中显式定义了构造函数:
- 要么必须在其中调用 super()
- 要么必须在其中返回一个对象
- 只能在派生类 构造函数 和 静态方法 中使用,实例方法中不能使用
- 不能单独引用,要么用它调用构造函数,要么用它引用静态方法
- 调用 super() 时,会调用父类构造函数,并将返回的实例赋值给 this
- 模式: