设计模式应该是一个工程化的考量,而不是一种技术强度,是一个技术与工程化的融合,那么在学习设计模式之前,我们需要知道关于继承的相关的知识。本章节希望读者有一定的js基础。好了,下面我们就开始讲解继承,来为未来的设计模式做一个基础。
在js里面的继承是比较特殊的,因为他没有专门的继承机制,而仅有的继承方式是依托构造函数的原型链。那么我们先解释一下原型。
大家在构造函数的属性里面能找到一个叫做prototype的属性,这个就是原型,当然在每个构造函数里面都有一个内置原型对象,根据这两个内容,可以在baidu上搜索相关知识,便知道什么意思了,我们接下来便开始说继承了。
function sup(){
this.book=[1,2,3];//公有属性
this.say=function(){
console.log(this.book);//公有方法
}
}
sup.prototype.talk=function(){
console.log(this.book);//原型方法
}
这是一个sup类,也就是一个构造函数,那么此时我们有一个obj也想要这种方法,那我们怎么去继承呢?
第一种方法,叫做call /apply继承,如下:
var obj={};
sup.call(obj);
那么我们此时使用
obj.say();//[1,2,3]
但是此时我们用
obj.talk();//报错
这样就会报错,因为,我们虽然用call改变了构造函数的执行环境,但是却没有继承sup原型链上面的方法。那看来,这个方式的缺点还是很大的。那么看看下面一种;
function sub(){}
sub.prototype=new sup();
这种方法我们定义了一个新的构造函数,我们让这个新的sub构造函数的prototype添加了sup的里面的方法;我们来看看效果吧
var obj=new sub();
obj.say();//[1,2,3];
obj.talk();//[1,2,3];
这样就好多了,但是却发现了这样的一个问题
var obj1=new sub();
obj1.book.push(2);
obj1.say();//[1,2,3,2];
obj.say();//[1,2,3,2];
什么?我们改变了新的实例化的对象的属性,却也把旧的也改变了?这在程序设计中是很可怕的。为什么呢,因为我们子类是把父类的属性添加到子类的原型中,这些都是通过子类new出来的实例都共享的,所以,就会导致这样的结果了啊,那么我们有办法杜绝这一点嘛?有啊。刚刚我们不是用过call了嘛?来试试call喝构造函数的融合体吧。
function sup(){
this.book=[1,2,3];//公有属性
this.say=function(){
console.log(this.book);//公有方法
}
}
sup.p rototype.talk=function(){
console.log(this.book);//原型方法
}
function sub(){
sub.call(this);
};
var obj1= new sub();
var obj2= new sub();
obj1.say();//[1,2,3];
obj2.say();//[1,2,3];
obj1.book.push(3);
obj1.say();//[1,2,3,3];
obj2.say();//[1,2,3];
子类的实例集合中通过更改父类的属性,也不会导致子类实例的改变,这样就是完美的嘛?不是。我们通过构造函数时执行了一遍父类的构造函数,又在子类实例进行实例化的时候又执行了一遍父类的构造函数,这样的继承是不完整的。那么我们有办法解决嘛?既然有问题,那么就肯定有解决的办法。
我们重回我们为什么要用继承。
1.我们需要父类里面的公有方法
2.我们需要父类里面的原型方法
那以上的继承,
第一个,只拿到了父类的公有方法,
第二个,我们把父类的公有方法和原型里面的方法都拿到了,但是都添加到了原型链里面了,导致子类的实例都是共享里面的属性,但是子类实例里面的方法和属性都是在原型链里面的,一个改动都会导致所有子类实例都会改变。
第三个,我们把父类里面的方法和原型都给了子类,然后又把父类里面的方法和属性在子类实例里面又执行了一遍,这样我们使用的时候都没有错,只是,父类里面的属性和方法又都执行了一遍。
那么既然这样的话,我们只需要父类里面的方法,父类原型里面的方法属性。
那我们重新把之前的东西整理一下
function sup(){
this.book=[1,2,3];//公有属性
this.say=function(){
console.log(this.book);//公有方法
}
}
sup.prototype.talk=function(){
console.log(this.book);//原型方法
}
function sub(){
sub.call(this);
};
更具以上的,我们拿到了父类的构造函数里面的东西,但是原型里的还没拿到呢
var p=sub.constructor;//获取子类的函数指向
sub.prototype=sup.prototype;//拿到父类的原型属性方法
sub.prototype.constructor=p;//形成一个闭环
那么这样我们就完成了一个继承比较好的方式。
那么我们整理好就是如下
function Super(){
this.books=[1,2,3];
this.age=1;
}
function Sub(){
Super.call(this);//主动调用父类的构造函数
}
//Sub.prototype=new Super();
Sub.prototype = Object.create(Super.prototype);//ie9+
/* ie9以下用下面代码
var Bs = function(){};
Bs.prototype = SuperClass.prototype;
SubClass.prototype = new Bs();
*/
Sub.prototype.constructor = Sub;
好了,这节就算是结束了。