上部分主要介绍高阶函数的常见形式,本部分将着重介绍高阶函数的高级应用。

  1.currying

  currying指的是函数柯里化,又称部分求值。一个currying的函数会先接受一些参数,但不立即求值,而是继续返回给另一个函数,通过闭包存储起来。等到函数被真正需求要求值的时候,将之前传入的参数统一起来求值。例如,我们要计算一个月的开销,我们并不需要计算每天具体花了多少,而是需要计算月底总共花掉多少,也就是说,实际上我们只需要在月底计算一次。所以每个月的前29天,我们都只需要保存好当天的开销,到30天时进行计算。我们可以借助currying函数实现。

var currying = function(fn){
var args = []; return function(){
//当参数为空时执行fn
if(arguments.length === 0){
return fn.apply(this, args);
}else{ //有参数时,不计算,将参数存放至args
[].push.apply(args, arguments);
return arguments.callee;
}
}
} var cost = (function(){
var money = 0; return function(){
for(var i = 0, len = arguments.length; i < len; i++){
money += arguments[i];
}
return money;
}
})() var cost = currying(cost);
cost(100); //第一天的花费,不计算真正的值
cost(200); //第二天的花费,不计算真正的值
cost(300); //第三天的花费,不计算真正的值
cost() //输出600,输出前三天的值

  2.函数节流

在某些场景下,函数有可能被非常频繁地调用,而造成性能问题。常见的场景有:

1.window.onresize事件,当给浏览器绑定resize事件浏览器窗口改变时,这个事件被触发的频率非常高,如果window.onresize事件中处理一些与DOM相关的操作,将造成非常大的性能影响,有可能造成浏览器卡顿;           2.mousemove事件,如果给一个div绑定了mousemove事件,在拖拽过程中往往也会频繁触发该事件;

3.百度搜索输入框,用户输入内容,页面做出相应的搜索,通过绑定keydown事件,如果不作处理,将请求地非常频繁。

  上面几个场景的共同问题是函数被触发的频率太高,我们可以通过忽略一部分事件请求来完成函数节流,如window.onresize中,拖动窗口一秒钟可能触发了10次,但是我们只取其中的两三次。可以通过setTimeout来完成这件事。

var throttle = function(fn, interval){
var timer, firstTime = true; return function(){
var args = arguments,
me = this; //如果是第一次,则不需要延迟
if(firstTime){
fn.apply(me, args);
return firstTime = false;
} //如果定时器还在,说明前一次的延迟执行还未完成
if(timer){
return false;
} timer = setTimeout(function(){
clearTimeout(timer);
timer = null;
fn.apply(me, args);
}, interval)
}
} window.onresize = throttle(function(){
console.log('Window onresize!!!')
}, 1000)
//测试结果:1秒内只会弹出一次Window onresize!!!

  3.分时函数

  场景如:WebQQ中的好友列表。列表中通常有几百上千好友,如果一个好友用一个节点表示,当我们要渲染这个列表时,可能需要一次性忘页面中创建成百上千个节点,在短时间内往页面中大量添加DOM节点可能会让浏览器卡顿甚至假死。可以如下代码表示:

var arr = [];

for(var i = 0; i < 1000; i++){
arr.push(i)
}; var renderFriendList = function(data){
for(var i = 0, l = data.length; i < l; i++){
var div = document.createElement('div');
div.innerHtml = i;
document.body.appendChild(div);
}
}; renderFriendList(arr);

这个问题的解决方案是采用分时函数,将创建节点分时进行,原来一秒钟创建1000个节点,改为每隔200ms创建10个节点,代码如下:

var timeChunk = function(arr, fn, count, time){
var obj, timer, len = arr.length; var start = function(){
for(var i = 0; i < Math.min(count || 1, arr.length); i++){
var obj = arr.shift();
fn(obj);
}
}; return function(){
timer = setInterval(function(){
if(arr.length === 0){
return clearInterval(timer);
timer = null;
}
start();
}, time)
};
};

  假设我们依然有1000个好友,我们可以利用timeChunk函数,每200ms插入一批,一批只往页面中添加10个元素;

var arr = [];

for(var i = 0; i < 1000; i++){
arr.push(i)
}; var renderFriendList = timeChunk(arr, function(n){
var div = document.createElement('div');
div.innerHtml = n;
document.body.appendChild(div);
}, 10, 200); renderFriendList();

  4.惰性加载函数

  在Web开发中,在IE、Chrome、FireFox中事件绑定方法不一样,我们通常会这么写:

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条件分支,显然这样并不是最佳实践,可以使用一些方法避免这些重复的执行过程。

  第二种方案是,我们把嗅探浏览器的操作提前到代码加载的时候,在加载的时候就立刻进行一次判断,以便让addEvent返回一个包裹了正确逻辑的函数,代码如下:

var addEvent = function(){
if(window.addEventListener){
return function(elem, type, handler){
elem.addEventListener(type, handler, false);
}
}
if(window.attachEvent){
return function(elem, type, handler){
elem.attachEvent('on' + type, handler);
}
}
}();

  目前的addEvent相比之前多次执行if有较大改善,但是仍然存在缺点:如果我们从头到尾都没使用addEvent这个函数,那么一开始多嗅探浏览器将是多余的操作,这将稍稍延长页面ready的时间。

  第三种方案是惰性载入函数方案。此时的addEvent依然被声明为一个普通函数,在函数里依然有一些分支判断。但是在第一次进入条件分支之后,在函数内部会重写这个函数,重写之后的函数就是我们期望的addEvent函数,在下一次进入addEvent的时候,将不存在条件分支语句:

var addEvent = function(elem, type, handler){
if(window.addEventListener){
addEvent = function(elem, type, handler){
elem.addEventListener(type, handler, false);
}
}
if(window.attachEvent){
addEvent = function(elem, type, handler){
elem.attachEvent('on' + type, handler);
}
} addEvent(elem, type, handler);
};

  小结:高阶函数和闭包结合使用使得JavaScript更灵活,掌握高阶函数将加深我们对JavaScript编程的理解。

JavaScript设计模式与开发实践——读书笔记1.高阶函数(下)的更多相关文章

  1. JavaScript设计模式与开发实践——读书笔记1.高阶函数(上)

    说来惭愧,4个多月未更新了.4月份以后就开始忙起来了,论文.毕设.毕业旅行等七七八八的事情占据了很多时间,毕业之后开始忙碌的工作,这期间一直想写博客,但是一直没能静下心写.这段时间在看<Java ...

  2. Javascript设计模式与开发实践读书笔记(1-3章)

    第一章 面向对象的Javascript 1.1 多态在面向对象设计中的应用   多态最根本好处在于,你不必询问对象“你是什么类型”而后根据得到的答案调用对象的某个行为--你只管调用行为就好,剩下的一切 ...

  3. javascript设计模式与开发实践阅读笔记(6)——代理模式

    代理模式:是为一个对象提供一个代用品或占位符,以便控制对它的访问. 代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对 ...

  4. javascript设计模式与开发实践阅读笔记(5)——策略模式

    策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 我的理解就是把各种方法封装成函数,同时存在一个可以调用这些方法的公共函数.这样做的好处是可以消化掉内部的分支判断,使代码效率 ...

  5. javascript设计模式与开发实践阅读笔记(8)——观察者模式

    发布-订阅模式,也叫观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 在JavaScript开发中,我们一般用事件模型来替代传统的观察者模式. ...

  6. javascript设计模式与开发实践阅读笔记(7)——迭代器模式

    迭代器模式:指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺 ...

  7. javascript设计模式与开发实践阅读笔记(4)——单例模式

    定义 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 具体来说,就是保证有些对象有且只有一个,比如线程池.全局缓存.浏览器中的window 对象等.在js中单例模式用途很广,比如登录 ...

  8. 《JavaScript设计模式与开发实践》笔记第八章 发布-订阅模式

    第八章 发布-订阅模式 发布-订阅模式描述 发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 发布-订阅模式可以广泛应用于 ...

  9. 《JavaScript设计模式与开发实践》笔记第一章

    第一章 面向对象的JavaScript 动态类型语言和鸭子类型 编程语言按照数据类型大体可以分为两类:静态类型语言.动态类型语言. 静态类型语言:在编译时便已确定变量的类型. 优点: 在编译时就能发现 ...

随机推荐

  1. vista风格的cms企业html后台管理系统模板——后台

    链接:http://pan.baidu.com/s/1c1Cv99e 密码:20yz

  2. Thinkphp的自定义路由(route.php)

    废话:因为thinkphp的默认路由会导致URL特别长,从而会影响搜索引擎优化.所以就衍生了自定义路由,尽量将URL缩短. 这是默认的路由文件: <?php return [ '__patter ...

  3. Ubuntu 各版本的几个国内更新源

    Ubuntu 国内更新源(各版本通用) 前言:为了下载更方便,速度更快,我们在使用Linux系列系统时修改 apt源 为国内的源 1.复制源文件备份,以防万一 修改文件sources.list,在目录 ...

  4. SilverLight 浏览器出现滚动条

    照网上说的很多解决方案要不得,最后想了下,直接在body上面加 style="overflow:hidden"解决问题,真觉得微软管理混乱,很多它自己的东西都不支持了.

  5. Linux时间结构体和获得时间函数

    关于Linux下时间编程的问题: 1. Linux下与时间有关的结构体 struct timeval { int tv_sec; int tv_usec; }; 其中tv_sec是由凌晨开始算起的秒数 ...

  6. 写在Web考试后的一点小总结

    在实验室折腾附加题折腾了一个多钟没做出来……晚上回到宿舍决定再试一试,按原来的思路居然行了,目测在实验室的时候什么地方打错字了吧(心在流血) 实现晃过元素后出现跟随鼠标的悬浮窗,只有几行代码给我折腾了 ...

  7. 第 17 章 使用API

    在本章中,我们将学习如何编写一个独立的程序,并对其获取的数据进行可视化.这个程序将使用Web应用编程接口(API)自动请求网站的特定信息而不是整个网页,再对这些信息进行可视化.由于这样编写的程序始终使 ...

  8. python ConfigParser 配置读写

    我们写程序的时候一般都会写程序包的时候,很少硬编码写在一起,这有个很好的配置文件包. 参考 ConfigParser  和   ConfigParser 的使用 一.ConfigParser 简介 C ...

  9. awk书上练习

    文件car: plym fury chevy malibu ford mustang volvo s80 ford thundbd chevy malibu bmw 325i honda accord ...

  10. python使用smtplib库和smtp.qq.com邮件服务器发送邮件

    使用qq的邮件服务器需要注意的两个地方主要是: 1.协议问题 使用465端口 SSL 协议 2.口令问题 出现SMTPAuthenticationError 主要的原因就是口令和帐号信息不对,这里我们 ...