原型模式是一种创建型对象,他允许一个对象再创建另外一个可定制的对象,无需知道任何如何创建的细节。并且原型实例指向创建对象的类,创建出来的类可以共享继承实例的属性和方法。
它主要面对的问题是:“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是他们却拥有比较稳定一致的接口。
function LoopImages( imgArr,container ) {
this.imgArr = imgArr
this.container = container
// 创建轮播图片
this.createImage = function() {
console.log("LoopImages createImage function");
}
// 切换下一张图片
this.changeImage = function() {}
}
function SlideLoopImage( imgArr,container ) {
LoopImages.call( this,imgArr,container )
// 重写继承的切换下一张图片方法
this.changeImage = function() {
console.log( "SlideLoopImage changeImage function" );
}
}
function FadeLoopImage( imgArr,container,arrow ) {
LoopImages.call( this,imgArr,container )
// 切花箭头私有变量
this.arrow = arrow
// 重写继承的切换下一张图片方法
this.changeImage = function() {
console.log( "FadeLoopImage changeImage function" );
}
}
var FadeImg = new FadeLoopImage(
[
'01.jpg',
'02.jpg',
'03.jpg',
'04.jpg'
],
'slide',
[
'left.jpg',
'right.jpg'
]
)
FadeImg.createImage()
FadeImg.changeImage()
如上面的代码,我们去考虑轮播图的时候,它的切换效果可能不止一种,但是在创建轮播图的过程中,有些步骤是相同的,如果没一个过程都去写一份代码,就会导致代码过于臃肿。我们先可以实现一个 LoopImages 类让部分逻辑可以复用,并在不同的需求中改写属性和方法,此时 LoopImages 就可以当作基类来使用。
原型 prototype
上面的例子中,并不是最好的解决方案。基类 LoopImages 是需要被子类继承的,但是此时的基类属性和方法都写在一起的。比如每次子类都继承了基类,但是基类的构造函数在创建的时候消耗了大量的时间,或者说每次构造的时候都做了一些重复性的事情,就会对性能产生影响。
为了提高性能,我们需要一种共享机制,这样每当创建基类时,我们把一些简单而差异化的属性放在构造函数中,而将一些消耗支援比较大的方法放在基类的原型中,就可以避免很多没必要的消耗。
function LoopImage( imgArr,container ) {
this.imgArr = imgArr
this.container = container
}
LoopImage.prototype = {
// 创建轮播图
createImage:function() {
console.log( "createImage" );
},
// 切换下一张图片
changeImage:function() {
console.log("changeImage");
},
getContainer:function() {
console.log( this.container );
}
}
function SlideLoopImage( imgArr,container ) {
LoopImage.call( this,imgArr,container )
}
SlideLoopImage.prototype = new LoopImage()
// 改写继承下来切换图片的方法
SlideLoopImage.prototype.changeImage = function() {
console.log( "SlideLoopImage changeImage function" );
}
function FadeLoopImage( imgArr,container,arrow ) {
LoopImage.call( this,imgArr,container )
this.arrow = arrow
}
FadeLoopImage.prototype = new LoopImage()
FadeLoopImage.prototype.changeImage = function() {
console.log( "FadeLoopImage changeImage function" );
}
var FadeImg = new FadeLoopImage(
[
'01.jpg',
'02.jpg',
'03.jpg',
'04.jpg'
],
'slide',
[
'left.jpg',
'right.jpg'
]
)
FadeImg.createImage()
FadeImg.changeImage()
FadeImg.getContainer()
- 每一个函数数据类型( 普通函数,类 ) 天生带有
prototype( 原型 ) 属性,这个属性是一个对象数据类型的值。 - 并且在
prototype上浏览器天生给它加了一个属性constructor(构造函数),属性值是当前函数(类)本身 - 每一个对象数据类型(普通的对象、实例)也天生自带一个属性
proto,属性值是当前实例所属类的原型(prototype)
原型扩展
原型对象是一个共享的对象,那么不论父类的实例对象或者子类的继承.都是对它的一个指向引用。所以原型才会被共享,如果对基类的原型对象进行扩展,不论时子类还是父类的实例对象都会继承下来。
LoopImage.prototype = {
// ...
getContainer:function() {
console.log( this.container );
}
}
FadeImg.getContainer() // slide
原型继承
原型模式更多的使用在对对象的创建上。比如创建一个实例对象的构造函数比较复杂,或者耗时太长,可以通过 create 或者自己来实现创建对象。
create 继承
我们可以利用JavaScript特有的原型继承特性去创建对象的方式,也就是创建的一个对象作为另外一个对象的prototype属性值。原型对象本身就是有效地利用了每个构造器创建的对象。
var LoopImage = {
imgArr:[],
container:"",
createImage:function() {},
changeImage:function() {}
}
var FadeLoopImage = Object.create( LoopImage )
FadeLoopImage.changeImage = function() {
console.log( "FadeLoopImage changeImage function" );
}
FadeLoopImage.changeImage()
Object.create运行你直接从其它对象继承过来,使用该方法的第二个参数,你可以初始化额外的其它属性。例如:
var LoopImage = {
imgArr:[],
container:"",
createImage:function() {},
changeImage:function() {}
}
var FadeLoopImage = Object.create( LoopImage,{
imgArr:[
'01.jpg',
'02.jpg',
'03.jpg',
'04.jpg'
],
container:"slide",
arrow:[
'left.jpg',
'right.jpg'
]
})
FadeLoopImage.changeImage = function() {
console.log( "FadeLoopImage changeImage function" );
}
FadeLoopImage.changeImage()
这里,可以在Object.create的第二个参数里使用对象字面量传入要初始化的额外属性,其语法与Object.defineProperties或Object.defineProperty方法类似。它允许您设定属性的特性,例如enumerable, writable 或 configurable。
自己实现
如果你希望自己去实现原型模式,而不直接使用Object.create 。你可以使用像下面这样的代码为上面的例子来实现:
function prototypeExtend() {
var F = function(){}, // 缓存类,为实例化返回对象临时创建
args = arguments, // 模板对象参数
i = 0,
len = args.length
for( ;i<len;i++ ) {
// 遍历模板参数对象中的属性
for( var key in args[i] ) {
// 将这些属性复制到缓存类原型中
F.prototype[key] = args[i][key]
}
}
// 返回缓存类的实例
return new F()
}
var FadeImage = prototypeExtend( {
imgArr:[
'01.jpg',
'02.jpg',
'03.jpg',
'04.jpg'
],
container:"slide",
arrow:[
'left.jpg',
'right.jpg'
],
createImage:function() {
console.log( "FadeImage createImage function" );
},
changeImage:function() {
console.log( "FadeImage changeImage function" );
},
getContainer:function() {
console.log( this.container );
}
} )
FadeImage.createImage() // FadeImage createImage function
FadeImage.changeImage() // FadeImage changeImage function
FadeImage.getContainer() // slide
总结
原型模式可以让多个对象分享同一个原型对象的属性和方法,这也是一种继承方式,不过这种继承是不需要创建的,而是将原型对象分享给那些继承的对象。当然有时需要让每个继承对象独立拥有一份原型对象,此时我们就需要对原型对象进行复制。
由此我们可以看出,原型对象更适合在创建复杂对象时,对于那些需求一致在变化而导致我们对象结构不停改变,将那些比较稳定的属性和方法共用提取出来用继承的方法来实现。