题外话
前段时间面试中笔试题有这道题目:
请实现一个继承链,要求如下:
-
构造函数
A()
:构造函数中有consoleA
方法,可以实现console.log("a")
实例对象 a:a 可以调用consoleA
方法 -
构造函数
B()
:构造函数中有consoleB
方法,可以实现console.log("b")
实例对象 b:b 可以调用consoleA
和consoleB
方法 -
构造函数
C()
:构造函数中有consoleC
方法,可以实现console.log("c")
实例对象 c:c 可以调用consoleA
和consoleB
和consoleB
方法
ok,这个题目暂时搁置,再回到这个问题之前,我们先来看看 js 的继承方式
约定
// 父类
function Super() {
this.name = "parent0";
this.colors = ["red", "blue", "yellow"];
}
Super.prototype.sex = "男";
Super.prototype.say = function() {
console.log(" Oh,My God! ");
};
构造继承
原理
通过使用 call、apply 方法可以在新创建的对象上执行构造函数,用父类的构造函数来增加子类的实例
实现
function Sub() {
Super.call(this);
this.type = "sub";
}
var s = new Sub();
console.log(s.colors); //[ 'red', 'blue', 'yellow' ]
s.say(); //报错 sub.say is not a function
优缺点
- 优点:简单明了,直接继承超类构造函数的属性和方法
- 缺点:无法继承原型链上的属性和方法
原型链式继承(借用原型链实现继承)
实现
function Sub() {
this.type = "sub";
}
Sub.prototype = new Super();
var s1 = new Sub();
var s2 = new Sub();
sub1.colors.push("black");
console.log(s1.colors); //[ 'red', 'blue', 'yellow', 'black' ]
console.log(s2.colors); //[ 'red', 'blue', 'yellow', 'black' ]
sub1.say();
我们实例化了两个 Sub,在实例 s1 中为父类的 colors 属性 push 了一个颜色,但是 s2 也被跟着改变了。造成这种现象的原因就是原型链上中的原型对象它俩是共用的。这不是我们想要的,s1 和 s2 这个两个对象应该是隔离的,
组合继承
这里所谓的组合是指组合借用构造函数和原型链继承两种方式。
function Sub() {
Super.call(this);
this.type = "sub";
}
Sub.prototype = new Super();
var s1 = new Sub();
var s2 = new Sub();
s1.colors.push("black");
console.log(s1.colors); //[ 'red', 'blue', 'yellow', 'black' ]
console.log(s2.colors); //[ 'red', 'blue', 'yellow' ]
可以看到,s2和s1两个实例对象已经被隔离了,但这种方式仍有缺点。父类的构造函数被执行了两次,第一次是Sub.prototype = new Super(),第二次是在实例化的时候,这是没有必要的。那我们就继续优化吧!!
组合式继承优化1
function Sub(){
Super.call(this)
this.type = "sub";
}
Sub.prototype=Super.prototype
var s1=new Sub()
var s2=new Sub()
但是请看以下代码
console.log(s1.constructor.name);//Super
我们还可以用.constructor来观察对象是不是某个类的实例:从这里可以看到,s1的构造函数居然是父类Super,而不是子类sub,这显然不是我们想要的。
组合式继承优化2
function Sub(){
Super.call(this)
this.type = "sub";
}
Sub.prototype=Super.prototype
Sub.constructor=Sub
var s1=new Sub()
var s2=new Sub()
console.log(s1 instanceof Sub);//true
console.log(s1 instanceof Super);//true
console.log(s1.constructor.name);//Super
perfect!!
开题解答
回到开头的问题中来,
function A(){}
A.prototype.consoleA=function(){
console.log("a")
}
function B(){
A.call(this)
}
B.prototype.consoleB=function(){
console.log("b")
}
Object.assign(B.prototype,A.prototype)
function C(){
B.call(this)
}
C.prototype.consoleC=function(){
console.log("c")
}
Object.assign(C.prototype,B.prototype)