js设计模式准备之继承篇

设计模式应该是一个工程化的考量,而不是一种技术强度,是一个技术与工程化的融合,那么在学习设计模式之前,我们需要知道关于继承的相关的知识。本章节希望读者有一定的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;

好了,这节就算是结束了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值