此贴用于记录原型链相关的一些东西。函数都有 prototype,对象都有__proto__,一个函数的 prototype 和一个对象的__proto__就是原型,原型其实也是一个对象。一个函数的 prototype 和这个函数的示例对象的__proto__是同一个引用,即:
function A(){
}
let a = new A();
console.log(a.__proto__ === A.prototype); // 返回的是 true
当调用某一个对象的方法的时候,如果找不到这个对象的方法,则会去找这个对象的原型里的方法,如果再找不到,则继续找原型的原型的方法,一直往上找,这个就是原型链,如果都找不到,则会报错。
在引入 es6 的 extends 之前,函数的继承就是通过原型来实现的。下面记录一下我理解的继承,下面的继承参考的是 [JS 继承的实现方式][1],他那里有完整的继承方法。
一. 原型链继承
function A(name) {
this.name = name;
this.f1 = function () {
console.log(this.name + ‘ 正在做 f1’);
}
}
A.prototype.f2 = function () {
console.log(this.name + ‘ 正在做 f2’);
}
function B(name) {
this.name = name;
}
B.prototype = new A(”);
let b = new B(‘test’);
b.f1();
b.f2();
console.log(b.__proto__ === B.prototype); //true
console.log(b instanceof A);//true
console.log(b instanceof B);//true
优点:1. 简单,实现容易 2. 能访问父类所有的方法和属性 3. 实例是当前类的实例,也是父类的实例
缺点:1. 要给 B 的 prototype 新增方法必须要在 new A(”); 之后 2. 无法实现多继承 3. 没法向父类的构造函数传递参数
二. 构造继承
function A(name){
this.name = name;
this.f1 = function(){
console.log(this.name + ‘ 正在做 f1’);
}
}
A.prototype.f2 = function(){
console.log(this.name + ‘ 正在做 f2’);
}
function B(name) {
A.call(this, name);
}
let b = new B(‘test’);
b.f1();
// b.f2();// 会报错
console.log(b instanceof A);//false
console.log(b instanceof B);//true
优点:1. 可以使用父类的属性和方法 2. 可以实现多重继承,即可以 call 多个函数 3. 实例对象是当前类的实例,不是父类的实例缺点:1. 无法获取 A 的 prototype 的属性和方法 2. 只是子类的实例,不是父类的实例 3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能(此处应该是调用 call 的时候会生成父类的实例副本,具体的还得再研究研究)
三. 组合继承
function A(name){
this.name = name;
this.f1 = function(){
console.log(this.name + ‘ 正在做 f1’);
}
}
A.prototype.f2 = function(){
console.log(this.name + ‘ 正在做 f2’);
}
function B(name) {
A.call(this, name);
}
B.prototype = new A(”);
// 要修正 prototype 的 constructor,为什么要修正还有待研究
B.prototype.constructor = B;
let b = new B(‘test’);
b.f1();
b.f2();
console.log(b instanceof A);//true
console.log(b instanceof B);//true
优点:1. 包含了原型链继承和构造继承的优点 2. 解决了原型链继承的无法实现多继承和没法向父类的构造函数传递参数的缺点 3. 解决了构造继承无法获取 A 的 prototype 的属性和方法还有只是子类的实例,不是父类的实例的问题缺点:1. 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
四. 寄生组合继承
function A(name){
this.name = name;
this.f1 = function(){
console.log(this.name + ‘ 正在做 f1’);
}
}
A.prototype.f2 = function(){
console.log(this.name + ‘ 正在做 f2’);
}
function B(name) {
A.call(this, name);
}
(function(){
// 创建一个没有实例方法的类
var Super = function(){};
Super.prototype = A.prototype;
// 将实例作为子类的原型,这样就可以只获取 A 的 prototype 的属性了
B.prototype = new Super();
//
B.prototype.constructor = B;
})();
let b = new B(‘test’);
b.f1();
b.f2();
console.log(b instanceof A);//true
console.log(b instanceof B);//true
优点:1. 在组合继承的基础上,只生成了一份父类的实例,prototype 也只继承了父类的 prototype,没有继承私有的属性缺点:1. 实现复杂