javascript设计模式5高阶函数
何为高阶函数?
至少满足下列条件之一的函数:
函数可以作为参数被传递;函数可以作为返回值输出。
作为参数被传递,代表我们可以抽离一部分容易变化的业务逻辑,把这部分逻辑放在函数参数中,可以分离业务代码中变化与不变的部分。
其中很重要的一个应用场景就是回调函数。
作为返回值输出,相比当作参数传递,函数当作返回值输出的应用的场景可能更多,也可能体现函数式编程的巧妙。让一个函数继续返回一个可执行的函数,意味着运算过程是可延续的。
高阶函数实现AOP
AOP的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,例如日志统计、安全控制、异常处理等。把这些功能抽离出来之后,再通过动态织入的方式参入业务逻辑模块中。
这种做法的好处是显而易见的,首先是保持了核心业务模块的纯净和高内聚。其次是可以很方便的复用日志统计等功能模块。
java通过反射和动态代理实现,对于js而言,aop实现更简单。这是js与生俱来的能力。
本质是函数动态织入。实现技术有很多,下面是其中一种方式,通过扩展Function.prototype来实现
Function.prototype.before = function(beforefn){
var _self = this;// 保存原函数引用
return function(){ // 返回原函数和新函数的 代理函数
beforefn.apply(this,arguments)// 执行新函数,修正this
return _self.apply(this,arguments) // 执行原函数
}
}
Function.prototype.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(2)
}
func = func.before(function(){
console.log(1)
}).after(function(){
console.log(3)
})
func();
currying,又称部分求值。一个currying的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求职的时候,之前传入的所有参数都会被一次性用于求值。
var currying = function(fn){
var args = []
return function(){
if(arguments.length === 0){
return fn.apply(this, args)
}else {
[].push.apply(args, arguments)
sdf
}
}
}
var cost = (function(){
var money = 0
return funcion(){
for(var i = 0 , l = arguments.length; i < l; i++){
money += arguments[i]
}
return money;
}
})();
var cost = currying(cost)
cost(100)
cost(200)
cost()
uncurrying 我们常常会让类数组对象去借用Array.prototype的方法,这是call和apply最常见的应用场景之一。
一般来说Array.pototype应该只能操作array对象,但是通过使用call和apply我们可以动态的传入任意对象,当作方法中的this,这样的话,方法中的this就不再局限原来规定的对象,而是加以泛化并得到更广的适应性。
有没有办法把泛化this的过程提取出来呢? uncurrying就是用来解决这个问题的、
Function.prototype.uncurrying = function(){
var self = this;
return function(){
var obj = Array.prototype.shift.call(arguments)
return self.apply(obj, arguments)
}
}
var push = Array.prototype.push.uncurrying();
(function(){
push(arguments, 4)
console.log(arguments)
})(1,2,3)
uncurrying的另一种实现方式
Function.prototype.uncurrying = function(){
var self = this;
return function(){
return Function.prototype.call.apply(self,arguments)
}
}
var push = Array.prototype.push.uncurrying();
push(obj,4);
函数节流
在一些场景中,函数可能被非常频繁地调用,从而造成大的性能问题。例如window.onresize,mousemove在这些频繁调用事件中,如果操作dom节点,往往会造成浏览器吃不消或卡顿现象。
使用setTimeout可以做到函数节流。实现代码有许多中,一下是其中一个封装。
函数节流,是一种限制函数被频繁调用的解决方案。
var throttle = function(fn, interval){ // 需要被延迟执行的函数 延迟间隔
var self = fn,
timer,
firstTime = true;
return function(){
var args = arguments;
me = this;
if(firstTime){
self.apply(me,args)
return firstTime = false;
}
if(timer){
return false;
}
timer = setTimeout(function(){
clearTimeout(timer)
timer = null
self.apply(me,args)
},interval||500)
}
}
window.onresize = throttle(function(){
console.log(1)
},500)
分时函数
有些函数需要被主动调用,但是将不得不严重影响页面性能。例如在webqq中创建好友列表的时候,短时间内可能要在页面中添加大量dom节点。这回让浏览器吃不消。导致浏览器卡顿甚至假死。
这时候可以采用分时函数。让创建节点的行为在多个时间段内完成。
例如下面的timeChunk函数
var timeChunk = function(ary, fn , count){
var obj, t;
var len = ary.length;
var start = function(){
for(var i = 0 ; i < Math.min(count||1 , ary.length);i++){
var obj = ary.shift()
fn(obj)
}
}
return function(){
t = setInterval(function(){
if(len ==0){
return clearInterval(t)
}
start();
},200)
}
}
惰性加载函数
web开发中,因为浏览器差异,一些嗅探工作总是不可避免。比如我们需要一个在各个浏览器中通过的事件绑定函数addEvent(elem,type,handler)
常见写法为
var addEvent = function(elem,type,handler){
if(window.addEventListener){
return elem.addEventListener(type,handler,false)
}
if(window.attachEvent){
return elem.attachEvent('on'+type,handler)
}
}
这种函数的缺点是,每次调用addEvent都会执行if条件分之,虽然执行if分支开销不算大,但是也许有一些方法可以避免程序重复执行。
第二种方法是,把嗅探代码提前到代码加载时候,直接判断浏览器,然后初始化为相应浏览器的addevent
var addEvent = (function(){
if(window.addEventListener){
return elem.addEventListener(type,handler,false)
}
if(window.attachEvent){
return elem.attachEvent('on'+type,handler)
}
})()
缺点是可能addEvent从未被调用,却在加载时候执行了一次,无谓的增加了页面加载时间
第三种方法是,惰性加载
var addEvent = function(elem,type,handler){
if(window.addEventListener){
addEvent = elem.addEventListener(type,handler,false)
}
if(window.attachEvent){
addEvent = elem.attachEvent('on'+type,handler)
}
addEvetn(elem,type,handler)
}
这样便保证了第一次加载,嗅探一次浏览器,设置addEvent为相应浏览器中的事件绑定代码。以后则不用判断,直接使用该浏览器的事件绑定机制了。

浙公网安备 33010602011771号