根据JavaScript高级程序设计一书的第六章以及JavaScript面向对象编程总结。
之前总结了创建对象的方法,关于js的继承主要是依靠对象及其原型链完成的,继承方式有如下几种:
原型链继承
定义shape类构造函数以及twoDShape类构造函数,要使twoDShape类 继承 shape类,使用原型链方式继承就是直接使twoDShape.prototype=new Shape();
function Shape(){
this.name='Shape';
}
Shape.prototype.getName =function(){
alert(this.name);
}
function twoDShape(){
this.name='2D Shape';
}
twoDShape.prototype=new Shape();
twoDShape.prototype.constructor=twoDShape;
var myshape=new twoDShape();
myshape.getName();//2D Shape
这样twoDShape的实例myshape就可以继承到getName方法,但是原型链继承没有办法在不影响所有的对象实例的情况下向父类构造函数传递参数。
借用构造函数
利用函数的call或apply方法。
function Shape(name){
this.name=name;
this.getName =function(){
alert(this.name);
}
}
function twoDShape(){
Shape.call(this,'2D Shape');
}
var myshape=new twoDShape();
myshape.getName();//2D Shape
这样就可以直接调用Shape类的方法了,而且可以进行参数传递,但是方法会在生成实例的时候重复定义,而且无法继承到Shape.prototype中的方法或属性。
直接继承
令子类.prototype直接等于父类.prototype,即twoDShape.prototype=Shape.prototype.
function Shape(){
this.name='Shape';
}
Shape.prototype.getName =function(){
alert(this.name);
}
function twoDShape(){
this.name='2D Shape';
}
twoDShape.prototype=Shape.prototype;
var myshape=new twoDShape();
myshape.getName();//2D Shape
这样直接的赋值操作使得两个类的prototype是相同的,不推荐这样的继承方式。
组合继承
组合继承是指在子类构造函数中使用call或apply调用父类构造函数,并使子类.prototype成为父类的实例。
function Shape(name){
this.name=name;
}
Shape.prototype.getName =function(){
alert(this.name);
}
function twoDShape(){
Shape.call(this,'2D Shape');
}
twoDShape.prototype=new Shape();
twoDShape.prototype.constructor=twoDShape;
var myshape=new twoDShape();
myshape.getName();//2D Shape
这是最常用的一种继承方式,父类的构造函数可以在不影响实例的情况下传递参数,原型上的方法可以实现复用。
原型式继承
原型式继承是创建了object()函数,使得两个不相关的对象实现继承关系。
function object(o) {
var F=function () {};
F.prototype=o;
return new F();
}
原型式继承可以看成是直接继承的改进,object()函数创建了临时性构造函数,然后将传入的对象作为该构造函数的原型,然后返回这个临时对象的新实例,相当于对传入的对象进行了浅复制。
Shape={
name:'Shape',
getName:function(){
alert(this.name);
}
};
var twoDShape=object(Shape);
twoDShape.getName();//2D Shape
这里传递进去的Shape如果是函数,则其包含的属性必须是公有共享的,注意,这里继承的一定是一个对象,不论是函数对象还是普通对象,和借用构造函数继承类似,无法继承到Shape.prototype的属性和方法。
ES5中就规范了原型式继承,即Object.create()创建新对象,可以指定新对象的原型。
Shape={
name:'Shape',
getName:function(){
alert(this.name);
}
};
var twoDShape=Object.create(Shape,{
height:{
value:20,
writable:true
},
{
value:20,
writable:true
}
});
twoDShape.getName();//Shape
alert(twoDShape.height);//20
alert(twoDShape.width);//20
Object.create可以接收2个参数,第一个就是指定的要继承的对象,第二个就是创建新对象的时候自身的属性,第二个参数的格式是根据每个属性的描述符定义的,即数据属性和访问属性。当第一个参数为null或undefined时,在非严格模式下指向的是全局变量window,严格模式下指向它们自身。
寄生式继承
寄生式继承是和原型式继承是相似的,不同之处在于创建了一个用于封装继承过程的函数,函数中对于新创建的对象进行了增强,即添加了新的属性和方法。
function createShape(o){
var myshape=object(o);
myshape.width=20;
myshape.getWidth=function(){
alert(this.width);
}
return myshape;
}
Shape={
name:'Shape',
getName:function(){
alert(this.name);
}
};
var twoDShape=createShape(Shape);
twoDShape.getWidth();//20
寄生式继承在不是自定义类型和构造函数的情况下,可以添加想要的方法。在一般情况下,由于在函数内部添加方法,这样会重复定义该方法,做不到函数的复用。
寄生式组合继承
组合继承虽然是常用的一种继承方式,但是在父类的构造函数始终调用了2次,寄生式组合继承就是用来解决这个问题的,即创建临时性构造函数,利用空对象作为中介。
function inherit(child,parent) {
function F() {}
F.prototype = parent.prototype;
child.prototype=new F();
child.prototype.constructor=child;
}
function Shape(name){
this.name=name;
}
Shape.prototype.getName =function(){
alert(this.name);
}
function twoDShape(name,width){
Shape.call(this,name);
this.width=width;
}
inherit(twoDShape,Shape);
twoDShape.prototype.getWidth=function(){
alert(this.width);
}
var myshape=new twoDShape('2D Shape',20);
myshape.getName();//2D Shape
myshape.getWidth();//20
这样父类的构造函数只调用了1次,提高了效率,是引用类型比较理想的继承方式,YUI库中使用的就是这种继承方式。
寄生式组合继承与原型式继承的区别在于原型式继承中传递进去的是构造函数,即F.prototype=Shape,而寄生式组合继承中是F.prototype=Shape.prototype。
拷贝继承
拷贝继承分浅拷贝和深拷贝,浅拷贝就是将引用类型值得地址进行复制,当改变子类的引用类型值时,父类的也会跟着改变。
function extend(child,parent) {
var p=parent.prototype;
var c=child.prototype;
for(let i in p){
c[i]=p[i];
}
c.uber=p;//uber是c的一个属性,确保c与p的联系
return c;
}
深拷贝是将引用类型的值如数组和对象里的值进行深度复制。
function deepCopy(parent,child) {
var c=child||{};
for(let i in parent){
if(typeof parent[i]==='object'){
c[i] = (parent[i].constructor === Array) ? [] : {};
deepCopy(parent[i], c[i]);
}else{
c[i] = parent[i];
}
}
return c;
}
深度拷贝不影响父类的情况下修改子类的引用类型值,jQuery库使用的就是这种继承方法。