基本术语
本文中,proto === [[Prototype]]
原型链
基本思想:
- 构造函数生成的对象有一个指针(proto)指向构造函数的原型。
- 如果将构造函数1的原型指向另一个构造函数2的实例,则构造函数1的实例__proto__.proto 指向了构造函数2的原型。原型2和实例1之间构成了一条原型。
jsx
function Super() {};
function Sub() {};
Super.prototype.sayHi = function () { console.log('hi') };
Sub.prototype = new Super();
const supInstance1 = new Sub();
supInstance1.sayHi(); // hi
缺点
- Sub.prototype.constructor 被改变。
- Sub.prototype = new Sub(),new的过程可能会有副作用。
盗用构造函数(经典继承)
基本思想
- 在new一个构造函数1的时候,通过在构造函数1中调用另一个构造函数2来实现继承。
- 通过调用构造函数2,将构造函数2中的实例属性复制到构造函数1的实例中。
jsx
function Car(wheels) {
this.wheels = wheels;
}
function ElectricCar(wheels, type) {
Cat.call(this, wheels);
this.type = type;
}
缺点
- 子类的实例不能访问父类原型上的方法。
- 必须在构造函数中定义方法,因此函数(构造函数1)不能重用。
组合继承(伪经典继承)
基本思想
- 将子类原型指向父类实例,通过原型链来实现子类继承父类原型上的方法。
- 通过盗用构造函数来继承实例的属性。
jsx
function Super(title) {
this.title = title;
}
Super.prototype.sayHi = function () {
console.log(this.title, " <- Super title");
};
function Sub(superTitle, subTitle) {
Super.call(this, superTitle);
this.subTitlte = subTitle;
}
Sub.prototype = new Super();
const subInstance = new Sub("superTitle in instance", "subTitle in instance");
subInstance.sayHi(); // ****subtitle in instance <- this title****
/* subInstance结构类型
**{
"title": "superTitle in instance",
"subTitlte": "subTitle in instance",
[[Prototype]]: Super
}
--- 在[[Prototype]]:Super中
--- Super的结构类型
{
title: undefined,
[[Prototype]]: Object,
}**
*/
缺点
- 在构造函数2的原型中,有无用变量title:undefined
- 在进行原型链的链接时,会执行new Super() 过程,如果构造函数Super是一个有副作用的函数,会有不可预知的问题。(两次调用Super函数)
- 子类的原型的constructor属性指向丢失。
原型式继承
基本思想
对象间构造原型链实现属性的共享。
实现
es5的Object.create函数
jsx
// 返回一个对象,对象.__proto__ 指向 o
function objectCreate(o) {
function F() {};
F.prototype = o;
return new F();
}
寄生式继承
基本思想
- 通过工厂模式创建新对象,构造函数中通过原型式继承来获取目标对象的能力。
jsx
function createSub(originObject) {
const newObject = Object.create(originObject);
newObject.sayHi = function() { console.log('hi') };
return newObject;
}
const person = {
name: '张三',
friends: ['李四', '赵武', '甲一']
};
const personA = createSub(person);
personA.sayHi();
优缺点
???感觉没有使用的场景
寄生式组合继承(目前比较完美的解决方案)
基本思想
- 重写子构造函数的原型,将构造函数原型的[[prototype]]指向从默认Object改为父构造函数的原型。实现原型属性的继承。
- 在子构造函数调用父构造函数,实现实例属性的复用。
jsx
function Super(name) {
this.name = name;
}
Super.prototype.sayHi = function() { console.log(`hi this is super and name is ${this.name}`)};
function Sub(name, age) {
Super.call(this, name);
this.age = age;
}
Sub.prototype = Object.create(Super.prototype, {
constructor: {
value: Sub,
enumerable: false,
writable: true,
configurable: true,
},
});
// 这里同样可以用es6中的 setPrototypeOf 来设置原型链的链接
Sub.prototype.sayAge = function () { console.log(`the age of ${this.name} is ${this.age}`); }
const subInstance = new Sub('Sub instance', 12);
subInstance.sayHi(); // hi this is super and name is Sub instance
subInstance.sayAge(); // **the age of Sub instance is 12**
优缺点
- 功能上没有缺点
- 实现起来冗长
es6的继承
extends关键字
es6的继承本质上是es5继承的语法糖。
jsx
// 可以实现和寄生式组合继承完全相同的效果
class Super {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(`hi this is super and name is ${this.name}`)
}
}
class Sub extends Super {
constructor(name, age) {
super(name);
this.age = age;
}
sayAge() {
console.log(`the age of ${this.name} is ${this.age}`)
}
}
const subInstance = new Sub('Sub instance', 12);
subInstance.sayHi(); // hi this is super and name is Sub instance
subInstance.sayAge(); // **the age of Sub instance is 12**
参考数据:
- [1] [你不知道的JavaScript]
- [2] [JavaScript高级程序设计]
- [3] [[mdn](https://developer.mozilla.org/)](https://developer.mozilla.org/)