初涉JavaScript模式 (9) : 函数 【常用方式】
回调模式
上一篇,对JavaScript函数进行了大体的介绍,这一篇对一些在工作中经常遇到的情况进行扩展。
在工作中,我们经常遇到很多需求,比如现在有一个需求:
一栋10层的大楼,当我们在坐电梯时,电梯每上一层,每层的电梯显示屏上即时显示电梯当前所在的楼层。
这样我们可能不到1s,就想到了解决方案,只要电梯每上一层,把每一层的电梯显示屏数字 +1 ,代码核心如下:
```javascript
function processArrayA(nums, scalar) {
var arr = [];
nums.forEach(function(value) {
arr.push(value + scalar);
});
return arr;
}
var numsA = [1, 2, 3, 4, 5];
console.log(processArrayA(numsA, 1)); //[2,3,4,5,6]
```
每当电梯升一层,我们只要调用processArrayA()方法就可以完成需求,但是假如后续又加了需求(PM一般都这样,呵呵),希望我们在电梯下降的时候也 -1,这样也没事,我们马上写出了代码:
```javascript
function processArrayB(nums, scalar) {
var arr = [];
nums.forEach(function(value) {
arr.push(value - scalar);
});
return arr;
}
var numsA = [1, 2, 3, 4, 5];
console.log(processArrayB(numsA, 1)); //[0,1,2,3,4]
```
额,当我们迅速解决了需求后,该死的PM又来了(悲催),说这栋楼希望显示2倍层数(奇葩有木有。。),作为技术的我们还得做。。。额,于是我们又给出了代码:
```javascript
function processArrayC(nums, scalar) {
var arr = [];
nums.forEach(function(value) {
arr.push(value * scalar);
});
return arr;
}
var numsA = [1, 2, 3, 4, 5];
console.log(processArrayC(numsA, 2)); //[2,4,6,8,10]
```
额,到这时候,我们也知道PM的需求是无止境的(谁知道脑残的PM再能想出什么奇葩来),而作为工程师的我们也开始再思考如何能一劳永逸了(懒程序才是好程序),额,这时我们的回调模式就派上用场了,我们发现前面三段代码示例除了forEach里面的逻辑有变化,其他都是没有变化的,我们要是把这段代码逻辑隔离出来,不就OK了。代码如下:
```javascript
//回调模式
function processArrayB(nums, scalar, callback) {
var arr = [];
nums.forEach(function(value) {
arr.push(callback(value, scalar));
});
return arr;
} function add(value, scalar) {
return value + scalar;
} function subtract(value, scalar) {
return value - scalar;
}
function ride(value,scalar){
return value * scalar;
}
var numsB = [1, 2, 3, 4, 5];
console.log(processArrayB(numsB, 2, add)); //[3,4,5,6,7]
console.log(processArrayB(numsB, 5, subtract));//[-4,-3,-2,-1,0]
console.log(processArrayB(numsB, 2, ride));//[2,4,6,8,10]
```
这样我们顺利的把add,ride。。方法独立出来,作为callback的形式,让处理函数去调用,而每次增加需求,我们只需要简单的添加callback函数就OK了(简单情况下),相比这个简单的需求,有时候我们可能会遇到复杂N倍的代码逻辑,而这时候回调模式就显得格外重要。
回调模式的用法很常见,比如我们addEventListener的时候里面的callback,而在一些常见的库中就应用的更加广泛了,例如Jq的“钩子” ,这样的好处就是我们在coding的过程中,只需要关注核心代码的实现,而不必考虑到每一种业务逻辑(可以通过callback的方式),这样会让我们更方便的扩展,构建,自定义自己的组件。
暴露接口
在JavaScript中,我们花了很长时间写好的组件,我们不希望他被别人随意改动,但是如果完全封闭,那么就失去了组件的封装性,那组件也就没有丝毫意义可言了。但是如果我们整个的暴露所有代码实现,那么可能导致很多超出我们预料的问题,所以我们可以仿照Java的模式来做,暴露部分接口,这样不仅保证了组件的稳定,也可以达到封装的目的。就拿我大学期间的做的一个图书管理系统代码:
```javascript
//书籍分类
GLOBAL.comm.comnav=(function(){
var bindNav=function(navList){
$.each(navList,function(key,val){
$("#"+val.split("-")[0]+">span").click(function(){
$(this).siblings().each(function(){
$(this).find(".active").hide().siblings().find("[_tag="+$(this).attr("_tag")+"]").removeClass("red");
});
$(this).find(".active").show().siblings().find("[_tag="+$(this).attr("_tag")+"]").addClass("red");
showTag([val.split("-")[1],val.split("-")[2]],$(this).attr("_tag"));
});
});
};
var processNav=function(navId,commId,listId){
return navId + "-" + commId + "-" +listId;
};
var showTag=function(tagList,tagName){
$.each(tagList,function(index){
$("#"+tagList[index]).find("[_tag="+tagName+"]").show().siblings().hide();
});
};
return {init:bindNav,
newInstantce:processNav
}
})();
```
这是我当时基于JQ做的一个项目中部分代码(要喷,请轻点。。),在这段代码中我是基于return 来实现暴露接口,暴露了init和newInstantce,而其他方法对外是完全不可见的。而具体的调用代码如下:
```javascript
//推荐类型切换
index.comNavChg = function(){
GLOBAL.comm.comnav.init([GLOBAL.comm.comnav.newInstantce("hottabs","recommends","hotlist")]);
}
```
以上的调用代码只利用到了comnav中暴露的两个方法,而具体的代码实现对外是不可见的。以上代码由于我是包装了return的函数,所以他形成了一个闭包(通过闭包来访问内部数据),这也就变相的实现了private。
即时函数
在上面图书管理的实例代码中,大家可能注意到我所有的逻辑都是写在Global这个命名空间下,但是很多情况下,例如写JQ的插件的时候,我们并要保证他在别人引用时都能正常运行,所以我们就要考虑新的方法。即时函数时一个很好的解决方案,他一般用来解决只允许运行一次的情况,例如打开很多巨头网站在console上面显示招聘信息的代码,他并不和任何业务代码产生交集,也很好的防止了污染全局变量(因为他为里面的代码提供了一个作用域沙箱)。下面我们来看dnspod的效果:
很酷吧,我们来看看它的具体代码实现:
```javascript
(function(){
try{
if(window.console && window.console.log){
console.log(' Qv. \n ZBjIjv; ..,.. \n @yizero.mFLIEmQ0qFFzSuFFZZqFSl; \n yBzl;vvvczFIv;::,:,:::::::: @haohu. \n BZFCvvvc;:,::;;;;;;;:;:;;;; ;Zbu. \n 7BuNNv7;::;;;;:::,,,,,::::;; IBO. \n .D0zFv7;::;:,.:;v7jjVyCslv;:::;. .:jBF \n :BqVNvvv:::,;sCzZFuIysLsVzZZ0Fl;;:;;v;;,:0B \n bZVFlv7v,.vmOSBs ..;bB0z:,;;;;:.jB \n @kevensun.SBv BjQ: vEEq VBl,:;;;:.jB \n BFzFs;7vvZb sI 0b; ;bF B ,BI,:;;;:.BV \n BFVFvvvvvB. D. ;bCm: ;0Nb N; .B;:;;;;.lB \n bNzFvv7vlB. BZ q; EO8F R Bj #N.;;;;:;B \n 0ByF7vvv;bF BBBCb @Wwz 7qSBBD B;;;;,7B \n lRu7BNzV;vvvvBZ ;BBBBR ;B; vB bBBBB BE,:;;;,,BZ \n Eb:. ;BFzj;vvv;0EBBBBB;:D .#:RBBBBNRS,:;;;,:ZB \n mE.,,: ;REylvvvv;vLObBBbz NBBBBDC;.:::::;EB, \n B..,:::..SbFc;vvvv;:;vCENZOZNFZNSy0s;. ,,::;;IBB \n D8:.,:,, ;O#F;;;vvv;;;;v7ljjyjsv;,,,;;;;v;vsRBl ,vvvv; \n lDOl;vsu; :jbF7;;;;;vvvvvvvvvvvvvvvvv;v7V8BV ;bZsjsjzBl \n ;ySVlvEEc .BOBbZzlv;;;;;;;;;;;;;vvsjNEDbBl7suq;.,,,,.,B; \n ,IZBQc8EF0R @guohezou.08DRDQ#BsZ0 ...,:::::,.Ez \n BRV#OjzVFuDBbbbQENZZEZqSuLN8sqB.;;;,,,::::: DC \n .ZOBQSzIcv;RZ.vyIVCClvvyVzZBqB#jCuVBs,.,,,.vb, \n 7#;v;v;;;0s ;7qF:vvOQv, @arqiao. \n lq,;;;;;:EQ ;BEb R7;;,Ss ,:,, \n LBjuVSVCOB ,BEB; zBsyFzBy \n uBzVFzzNB ,BVmBbbBBc \n zBCuzuCBmvzZOQ8bBFO8v;; 7EC \n SBCICsLLVNZZZFFjIBF:;vv #Z \n .RNFzzy @kexianli.:;;;v7;;,0b \n BEuuIBj BOFCjcllyI#B. \n Bs;;;CB. :@secbone.b \n RZvv7;;vQR IBB#RbBy \n BZ;v7v,.. Bc :vv; \n RL;;vv;;:;NZ \n Fmv7l7ssIjqBBqv \n NBBOzIVVSu0bBbBBB \n jBbBBBbBbBBBBBBBB \n .vuqQ8DQEZuc; \n\n');
console.log('Hi~ 欢迎加入DNSPod,简历请发送至 %c hr@dnspod.com','color:#4FA6E7');
}
}catch(e){}
})()
```
额,虽然只是文字把戏,可是别人也做的很用心,这种代码用即时函数来做,是再好不过了(感兴趣的童鞋可以把上面的代码拉倒console里面去试试)。
下面我们来了解下即时函数的写法,代码如下:
```javascript
//第一种形式
(function() {
//body
})(); //第二种形式
(function(){ }());
```
上面2种模式没有太大的区别,我喜欢使用第一种(看上去更像是执行一个函数) ,但是JSLint偏好使用第二种。这种模式本质上只是一个函数表达式(详细请看上一篇)。
同样的,既然他也是函数,那么我们一样可以给他传参,在JQ插件中更多的是给他指定作用域。代码示例如下:
```javascript
(function(window, document, undefined) {
function Wr(element, opts) {
return (this instanceof Wr) ? this.init(element, opts) : new Wr(element, opts);
} Wr.prototype.init = function(){
// body ...
};
//other code
})(window, window.document);
```
同样的很多模块化代码的实现中,也大量利用到了即时函数(用以区分不同模块)。
结语
这一篇,我总结了下工作中包括了解到的一些函数的编程用法,总体的是想加强自己在JS结构方面的认识,希望加深自己对JS模块化的认识。如有错误,请指正。下一篇我将总结一下JS中函数的一些高级用法。
初涉JavaScript模式 (9) : 函数 【常用方式】的更多相关文章
- 初涉JavaScript模式 (8) : 函数 【概述】
什么是函数 函数,是一个大型程序中的某部份代码,由一个或多个语句块组成.它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性.(维基百科) 函数的特点 第一类对象 在JavaScript世界中 ...
- 初涉JavaScript模式 (10) : 函数 【进阶用法】
写在前面 不知不觉写到第10篇了.这篇写起来很忐忑,终于和高级搭上边了(呵呵),这篇我们 主要 说一下 JS 方法的部分高级用法(我知道的),笔者水平有限,难免有错.废话不多少,进入正文. 初始化 我 ...
- 初涉JavaScript模式系列 阶段总结及规划
总结 不知不觉写初涉JavaScript模式系列已经半个月了,没想到把一个个小点进行放大,竟然可以发现这么多东西. 期间生怕对JS的理解不到位而误导各位,读了很多书(个人感觉JS是最难的oo语言),也 ...
- 【读书笔记】读《JavaScript模式》 - 函数复用模式之现代继承模式
现代继承模式可表述为:其他任何不需要以类的方式考虑得模式. 现代继承方式#1 —— 原型继承之无类继承模式 function object(o) { function F() {}; F.protot ...
- 【读书笔记】读《JavaScript模式》 - 函数复用模式之类式继承模式
实现类式继承的目标是通过构造函数Child()获取来自于另外一个构造函数Parent()的属性,从而创建对象. 1.类式继承模式#1 —— 默认方式(原型指向父函数实例) function Paren ...
- 初涉JavaScript模式 (11) : 模块模式
引子 这篇算是对第9篇中内容的发散和补充,当时我只是把模块模式中的一些内容简单的归为函数篇中去,在北川的提醒下,我才发觉这是非常不严谨的,于是我把这些内容拎出来,这就是这篇的由来. 什么是模块模式 在 ...
- 初涉JavaScript模式 (4) : 构造函数
什么是构造函数? 构造函数 是一种特殊的方法 主要用来在创建对象时初始化对象 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 特别的一个类可以有多个构造函数 可根据其参数个数的不 ...
- 初涉JavaScript模式 (2) : 基本技巧
尽量少用全局变量 大量使用全局变量会导致的后果 全局变量创建以后会在整个JavaScript应用和Web页面中共享.所有的全局变量都存在于一个全局命名空间内,很容易发生冲突 不知不觉创建了全局变量 其 ...
- Javascript和JQuery函数定义方式
Javascript 函数定义方式 1.function show() { } 2.var cal = function() { },必须先声明才能调用 示例: <html> ...
随机推荐
- Qt入门(15)——使用窗口部件
下面是如何创建一个你自己的窗口部件,描述如何控制一个窗口部件的最小大小和最大大小,并且介绍了窗口部件的名称. class MyWidget : public QWidget { public: MyW ...
- 【模拟】Codeforces 704A & 705C Thor
题目链接: http://codeforces.com/problemset/problem/704/A http://codeforces.com/problemset/problem/705/C ...
- c++学习(1)
c++学习(1) 1.const C VS C++: 在c语言中const是一个只读变量(ReadOnly Varible),在c++中const只是代表常量(Constant). 例: const ...
- Ural1297 Palindrome(后缀数组)
[题目链接] http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=12406 [题意] 求最长回文子串. [思路] 将字符串 ...
- poj3261 Milk Patterns(后缀数组)
[题目链接] http://poj.org/problem?id=3261 [题意] 至少出现k次的可重叠最长子串. [思路] 二分长度+划分height,然后判断是否存在一组的数目不小于k即可. 需 ...
- Big Event in HDU(01背包)
/* 题意: 输入一个数n代表有n种物品, 接下来输入物品的价值和物品的个数: 然后将这些物品分成A B 两份,使A B的价值尽可能相等也就是尽量分的公平一些,如果无法使A B相等,那么就使A多一些: ...
- @protocol 和 category 中如何使用 @property
出题者简介: 孙源(sunnyxx),目前就职于百度 整理者简介:陈奕龙(子循),目前就职于滴滴出行. 转载者:豆电雨(starain)微信:doudianyu 在 protocol 中使用 prop ...
- 【Cocos2d-X开发学习笔记】第26期:游戏背景之滚动地图背景(CCParallaxNode)的使用
本系列学习教程使用的是cocos2d-x-2.1.4(最新版为3.0alpha0-pre) ,PC开发环境Windows7,C++开发环境VS2010 在现实生活中,当我们身处一辆快速行驶的车上,用眼 ...
- 如何定制Windows系统右键菜单
今天心血来潮把几个自己常用的工具定制到了系统的右键菜单.包括notepad++,7zip,还有复制文件全路径和文件夹路径.下面简单介绍一下步骤. 1. Windows系统右键菜单对应的注册表位置 Wi ...
- mongo 多条件 查询
var query1 = Query<BaseManagerForCompanyModel>.EQ(q => q.sGuidBaseCompany, sGuidBaseCompany ...