网站链接: element-ui dtcms
当前位置: 首页 > 技术博文  > 技术博文

高阶函数 与设计模式之 装饰者模式

2021/6/9 21:06:00 人评论

一、定义: 高阶函数是指至少满足下列条件之一的函数。  函数可以作为参数被传递;  函数可以作为返回值输出。 二、高阶函数实现AOP(面向切面编程) 通常,在 JavaScript 中实现 AOP,都是指把一个函数“动…

一、定义:
高阶函数是指至少满足下列条件之一的函数。
 函数可以作为参数被传递;
 函数可以作为返回值输出。

二、高阶函数实现AOP(面向切面编程)
通常,在 JavaScript 中实现 AOP,都是指把一个函数“动态织入”到另外一个函数之中,具 体的实现技术有很多,本节我们通过扩展 Function.prototype 来做到这一点。代码如下:

Function.propotype.before = function(beforefn){
	var _self = this;
	return function(){
		beforefn.apply(this, arguments);
		return _self.apply(this, arguments);
	}
}

Function.propotype.after = function(afterfn){
	var _self = this;
	return function(){
		var ret= _self.apply(this, arguments);
		afterfn.apply(this, arguments);
		return ret;
	}
}

var func = function(){
	console.log(1)
}
func = func.before(function(){
	console.log(2)
}).after(function(){
	console.log(3)
})

// 2 1 3

这种使用AOP 的方式,来实现装饰者的模式。

三、装饰者模式(decorator)
在传统的面向对象语言中,给对象添加功能常常使用继承的方式,但是继承的方式并不灵活。

现在需要 一个办法,在不改变函数源代码的情况下,能给函数增加功能,这正是开放-封闭原则给我们指 出的光明道路。

比如我们想给 window 绑定 onload 事件,但是又不确定 这个事件是不是已经被其他人绑定过,为了避免覆盖掉之前的 window.onload 函数中的行为,我 们一般都会先保存好原先的 window.onload,把它放入新的 window.onload 里执行:

window.onload = function(){    
 alert (1); 
}

var _onload = window.onload || function(){};

window.onload = function(){     
	_onload();     
	alert (2); 
}

这个的有俩个问题,一个是如果装饰链过长,就是需要维护的_onload 的变多。二就是this的被劫持问题。

所以就用上面的 AOP装饰函数。

Function.prototype.before = function( beforefn ){    
 var __self = this;  // 保存原函数的引用     
 return function(){    // 返回包含了原函数和新函数的"代理"函数         
   beforefn.apply( this, arguments );  
   // 执行新函数,且保证 this 不被劫持,新函数接受的参数                                            
    // 也会被原封不动地传入原函数,新函数在原函数之前执行         
   return __self.apply( this, arguments );  
   // 执行原函数并返回原函数的执行结果,并保证this不被劫持 
   } 
   }
Function.prototype.after = function( afterfn ){    
 var __self = this;     
 return function(){         
 	var ret = __self.apply( this, arguments );         
 	afterfn.apply( this, arguments );         
 	return ret;     
 } 
 };

四、实际应用

4.1 数据统计上报(埋点)

<html>    	
 <button tag="login" id="button">点击打开登录浮层</button>     
 <script>
    Function.prototype.after = function( afterfn ){        
     var __self = this;        
      return function(){             
        var ret = __self.apply( this, arguments ); 
        afterfn.apply( this, arguments );             
        return ret;         
      }     
    };
    var showLogin = function(){         
       console.log( '打开登录浮层' ); 
       // 如果不用这种 装饰分离思想,直接调用log()
       // log(); 
    }
    var log = function(){         
       console.log( '上报标签为: ' + this.getAttribute( 'tag' ) );    
    }
    showLogin = showLogin.after( log );    // 打开登录浮层之后上报数据
    document.getElementById( 'button' ).onclick = showLogin;     </script> 
    </html>

4.2 表单验证

无设计模式的写法:

<html>     
  <body> 
      用户名:<input id="username" type="text"/>
    密码:  <input id="password" type="password"/>     
    <input id="submitBtn" type="button" value="提交"> 
    </body> 
  <script> 
   var username = document.getElementById( 'username' ),
       password = document.getElementById( 'password' ),
       submitBtn = document.getElementById( 'submitBtn' );
      
   var formSubmit = function(){
  		if ( username.value === '' ){
   	  		return alert ( '用户名不能为空' );   
   		}   
   		if ( password.value === '' ){
      		return alert ( '密码不能为空' );   
    	}
    
  		var param = {
   			username: username.value,
   			password: password.value   
   		}   
   		ajax( 'http:// xxx.com/login', param );    // ajax 具体实现略
   }
   submitBtn.onclick = function(){
     	formSubmit();  
   }
 </script> 
</html>
// formSubmit 函数在此处承担了两个职责,除了提交 ajax 请求之外,还要验证用户输入的合法 性。这种代码一来会造成函数臃肿,职责混乱,二来谈不上任何可复用性

优化, 使validata 与 formsubmit 完全分离

Function.prototype.before = function( beforefn ){   
 var __self = this;     
 return function(){         
	 if ( beforefn.apply( this, arguments ) === false ){            
	  // beforefn 返回 false 的情况直接 return,不再执行后面的原函数  
	  return;         
	  }         
  	return __self.apply( this, arguments );     
  } 
}
var validata = function(){     
	if ( username.value === '' ){        
 		alert ( '用户名不能为空' );        
  		return false;     
   }     
   if ( password.value === '' ){        
   		alert ( '密码不能为空' );        
    	return false;     
    } 
}
var formSubmit = function(){     
	var param = {         
		username: username.value,         
		password: password.value     
	}    
 	ajax( 'http:// xxx.com/login', param ); 
}
formSubmit = formSubmit.before( validata );
submitBtn.onclick = function(){     
	formSubmit(); 
}

//validata 成为一个即插即用的函数,它甚至可以被写成配置文件的形式,这有利于我们分 开维护这两个函数。再利用策略模式稍加改造,我们就可以把这些校验规则都写成插件的形式, 用在不同的项目当中。

相关资讯

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?