咱们就拿《head first 设计模式》中的一个例子入手,使用面向对象的编程思想:html
咱们有一家咖啡店,暂定每杯咖啡售价10元
function Coffee() { this.price = 10 } Coffee.prototype.getPrice = function () { return this.price } var coffee = new Coffee() console.log(coffee.getPrice());
如今新进了一批配料(摩卡,蓝莓,奶泡)分别对应价钱为(5,3,4),那么咱们要怎么计算一杯摩卡咖啡的价钱呢?
1.新建一个摩卡咖啡的对象:编程
function MochaCoffee() { this.price = 15 } MochaCoffee.prototype.getPrice = function () { return this.price } var mochaCoffee = new MochaCoffee() console.log(mochaCoffee.getPrice());
2.使用继承设计模式
var coffee = new Coffee() function MochaCoffee() { this.mochaPrice = 5 } MochaCoffee.prototype = new Coffee() MochaCoffee.prototype.getPrice = function () { return this.price + this.mochaPrice } var mochaCoffee = new MochaCoffee() console.log(mochaCoffee.getPrice());
那么每增长一种配料,就须要生成多一个类。app
可是,不管是第一种办法,仍是第二种办法都很难知足一下的状况,好比函数
目前只有3种配料,咱们就须要添加3个新的类了,那么当咱们的配料添加,或者添加(咖啡根据大中小杯去计算价钱)这种需求时,就须要添加无数的类去实现它;性能
若是只是使用继承,那么当状况复杂时,类的数量就会呈爆炸式增加,这是咱们所不但愿见到的。this
使用构造器存储coffee对象的引用prototype
function Mocha(coffee) { this.price = 5 this.coffee = coffee } Mocha.prototype.getPrice = function () { return this.price + this.coffee.getPrice() } var coffee = new Coffee() var mochaCoffee = new Mocha(coffee) console.log(mochaCoffee.getPrice());
若是这时咱们须要点一份双倍摩卡的咖啡,直接以下调用:设计
var coffee = new Coffee() var mochaCoffee = new Mocha(coffee) var doubelMochaCoffee = new Mocha(mochaCoffee) console.log(doubelMochaCoffee.getPrice());
使构造器存储coffee对象的引用一点也不灵活,咱们作以下改动:代理
function Mocha() { this.price = 5 } Mocha.prototype.getPrice = function () { return this.price + this.coffee.getPrice() } Mocha.prototype.setCoffee = function (coffee) { this.coffee = coffee } var coffee = new Coffee() var mochaCoffee = new Mocha() mochaCoffee.setCoffee(coffee) console.log(mochaCoffee.getPrice());
如今就能够动态的设置coffee对象了!
若是你还想要一杯摩卡蓝莓咖啡也是很是容易实现的:
function Blueberry() { this.price = 3 } Blueberry.prototype.getPrice = function () { return this.price + this.coffee.getPrice() } Blueberry.prototype.setCoffee = function (coffee) { this.coffee = coffee } var coffee = new Coffee() var mochaCoffee = new Mocha() mochaCoffee.setCoffee(coffee) var mochaBlueberryCoffee = new Blueberry() mochaBlueberryCoffee.setCoffee(mochaCoffee) console.log(mochaBlueberryCoffee.getPrice());
window.onload = function () { alert(1) } var _onload = window.onload window.onload = function () { _onload() alert('new') }
这是实际开发中很常见的一种作法,好比咱们想给 window 绑定 onload 事件,可是又不肯定 这个事件是否是已经被其余人绑定过,为了不覆盖掉以前的 window.onload 函数中的行为,我 们通常都会先保存好原先的 window.onload,把它放入新的 window.onload 里执行,添加了一层简单的包装。
可是上面的作法会致使this丢失的问题,这里不会报错是由于全局变量就是window;
分别实现Function.prototype.before
和Function.prototype.after
,以下:
Function.prototype.before = function (beforefn) { var __self = this; // 保存原函数的引用 return function () { // 返回包含了原函数和新函数的"代理"函数 beforefn.apply(this, arguments); // 执行新函数,且保证 this 不被劫持,新函数接受的参数 // 也会被原封不动地传入原函数,新函数在原函数以前执行 return __self.apply(this, arguments); // 执行原函数并返回原函数的执行结果, 2 // 而且保证 this 不被劫持 } } Function.prototype.after = function (afterfn) { var __self = this; return function () { var ret = __self.apply(this, arguments); afterfn.apply(this, arguments); return ret; } };
数据统计上报功能:
<html> <button tag="login" id="button">点击打开登陆浮层</button> <script> var showLogin = function(){ console.log( '打开登陆浮层' ); log('上报信息'); } var log = function( tag ){ console.log( '上报信息为: ' + tag ); } document.getElementById( 'button' ).onclick = showLogin; </script> </html>
咱们看到在 showLogin 函数里,既要负责打开登陆浮层,又要负责数据上报,这是两个层面 的功能,在此处却被耦合在一个函数里。使用 AOP 分离以后,代码以下:
<html> <button tag="login" id="button">点击打开登陆浮层</button> <script> Function.prototype.after = function( afterfn ){ var __self = this; return function(){ var ret = __self.apply( this, arguments ); 9 afterfn.apply( this, arguments ); return ret; } }; var showLogin = function(){ console.log( '打开登陆浮层' ); } var log = function(){ console.log( '上报信息为: ' + 'balabalabla' ); } showLogin = showLogin.after( log ); // 打开登陆浮层以后上报数据 document.getElementById( 'button' ).onclick = showLogin; </script> </html>
在《设计模式》成书以前,GoF原想把装饰者(decorator)模式称为包装器(wrapper)模式。
其实装饰者模式,就是在原有的基础上添加了一层包装,造成一条包装链。
使用了装饰者模式,咱们能够动态的添加被装饰者的职责,对它进行非入侵式的行为修改,从而达到咱们要求而不须要建立更多的类。
缺点:当装饰的层数过深时,实际上做用域链也就变的很长,性能也会受到影响。