语言中的原型
JavaScript中的继承是靠原型链实现的,而原型模式就是将原型对象指向创建对象的类,使这些类共享原型对象的方法与属性,而不是对属性和方法的复制。
原型继承实例
不使用原型实现继承
首先我们来假设一个场景:车的类型有很多,比如卡车、公交车、轿车、火车等。每种车都有不同的价格和速度以及功能。那现在就以交通工具为父类,轿车和卡车为子类,实现继承。
// 交通工具类
var Vehicle = function(price,speed){
this.price = price
this.speed = speed
this.getPrice = function(){
return this.price
}
this.changePrice = function(){
}
}
// 汽车类
var Car = function(price,speed){
// 构造函数继承交通工具类
Vehicle.call(this,price,speed)
// 重写修改价格方法,打9折
this.changePrice = function(){
return this.price * 0.9
}
}
// 卡车类
var Truck = function(price,speed,weight){
// 构造函数继承交通工具类
Vehicle.call(this,price,speed)
// 添加卡车私有变量
this.weight = weight
// 重写修改价格方法,打9折
this.changePrice = function(){
return this.price * 0.8
}
}
// 实例化一个汽车类
var BMW = new Car(6000000,100)
console.log('宝马')
console.log(BMW.changePrice())
console.log('奔驰')
var BenzTruck = new Truck(1000000,200,150)
console.log(BenzTruck.price)
console.log(BenzTruck.speed)
console.log(BenzTruck.weight)
console.log(BenzTruck.changePrice())
我们发现直接使用构造函数继承,将Vehicle(交通工具)为基类,是可以实现继承的。但是这样也会有一些问题,就是每次子类继承都要创建一次父类,当父类构造函数创建时存在很多耗时较长的逻辑,或者说每次创建的时候都要进行一些重复性的操作,这样其实对性能的消耗是挺大的。
所以我们才需要引入一种共享机制,这样每当创建基类时,我们将一些简单又差异化的属性可以直接放在构造函数中(比如this.price = price),而将一些消耗资源比较大的方法放在基类的原型中,这样我们就会避免很多不必要的消耗。
原型模式
// 交通工具类
var Vehicle = function(price,speed){
this.price = price
this.speed = speed
}
Vehicle.prototype = {
getPrice:function(){
return this.price
},
changePrice:function(){
}
}
// 汽车类
var Car = function(price,speed){
// 构造函数继承交通工具类
Vehicle.call(this,price,speed)
// 重写修改价格方法,打9折
this.changePrice = function(){
return this.price * 0.9
}
}
Car.prototype = new Vehicle()
// 卡车类
var Truck = function(price,speed,weight){
// 构造函数继承交通工具类
Vehicle.call(this,price,speed)
// 添加卡车私有变量
this.weight = weight
// 重写修改价格方法,打9折
this.changePrice = function(){
return this.price * 0.8
}
}
Truck.prototype = new Vehicle()
// 实例化一个汽车类
var BMW = new Car(6000000,100)
console.log('宝马')
console.log('继承的原型方法',BMW.getPrice())
console.log('重写的原型方法',BMW.changePrice())
console.log('奔驰')
var BenzTruck = new Truck(1000000,200,150)
console.log(BenzTruck.price)
console.log(BenzTruck.speed)
console.log(BenzTruck.weight)
console.log(BenzTruck.changePrice())
下面的这两段代码就是原型继承的精髓了。
Vehicle.prototype = {
getPrice:function(){
return this.price
},
changePrice:function(){
}
}
Car.prototype = new Vehicle()
原型继承
原型模式更多的是用在对对象的创建上,当创建一个实例对象的构造函数比较特殊,或者要继承多个基类时,此时最好不要用new关键字取复制这些基类,但可以通过对这些对象属性或者复制来实现创建。
function prototypeExtend(){
// 缓存类
let F = function(){
}
for(let i = 0;i < arguments.length;i++){
// 遍历各个模版对象的属性
for(let key in arguments[i]){
// 将这些属性复制到缓存类原型中
F.prototype[key] = arguments[i][key]
}
}
// 返回一个原型对象的实例
return new F()
}
var plugin = prototypeExtend(
{
price:100,
changePrice:function(){
return this.price * 0.9
}
},
{
speed:150,
changeSpeed:function(){
console.log('提不了速了!')
}
},
{
weight:200
}
)
console.log(plugin.changePrice())
plugin.changeSpeed()
console.log(plugin.weight)
什么时候用原型继承
**原型模式可以让多个对象分享同一个原型对象的属性和方法,这也是一种继承方式,这种继承方式的实现是不需要创建的,而是将原型对象分享给那些继承的对象。**当然有时候每个继承对象独立拥有一份原型对象,此时我们就需要对原型对象进行复制。
由此可以看出,原型对象更适合在创建复杂的对象时,对于那些需要 一直在变化而导致对象结构不停改变时,将那些比较稳定的属性与方法共用而提取的继承实现。
谈谈“原型继承的实现不需要了解创建的过程”的理解
通过以上的学习,我们可以知道原型继承的关键就是将父类的实例赋值给子类的原型prototype,而我们在实例化子类时,是不会对prototype进行new的操作的。换句话说,原型继承的精髓就是让子类的实例共享prototype上的属性和方法,减少消耗,而非重新复制或者创建一个新的对象,这也是原型继承的最初思想。
下一章 单例模式