在javascript中,当一个函数A做为另一个函数B的其中一个参数时,则称A函数为回调函数,即A能够在函数B的运行周期内执行(开始,中间,结束)。javascript
举例来讲,有一个函数用于生成node.php
var complexComutation = function(){ // 内部处理,并返回一个node }
又有一个findNodes函数声明用于查找全部节点,而后经过callback回调进行执行代码。html
var findNodes = function(callback){ var nodes = []; var node = complexComputation(); // 若是回调函数可用,则执行她 if(typeof callback === 'function'){ callback(node); } nodes.push(node); return nodes; }
关于callback的定义,咱们能够事先定义好来用:java
// 定义callback var hide = function(node){ node.style.display = 'node'; } var hiddenNodes = findNodes(hide);
也能够在调用的时候使用匿名定义,以下:node
// 使用匿名定义callback var blockNodes = findNodes(function(node){ node.style.display = 'block'; })
咱们平时用的最多的,估计就数jQuery的ajax方法的调用了,经过在done/faild上定义callback,以便在ajax调用成功或者失败的时候作进一步处理,代码以下(本代码基于jquery1.8版):jquery
var menId = $('ul.nav').first().attr('id'); var request = $.ajax({ url: 'script.php', type: 'post', data: {id: menuId}, dataType: 'html' }) // 调用成功时的回调处理 request.done(function(msg){ $('#log').html(msg); }) // 调用失败时的回调处理 request.fail(function(jqXHR, textStatus){ alert('Request failed:' + textStatus); });
若是一个函数(或方法)的参数只有一个,而且参数为对象字面量,咱们则称这种模式为配置对象模式。以下所述:ajax
var her = { username: 'gaohan', first: 'gao', last: 'han' }; addPerson(her);
则在addPerson函数内部,就能够随意使用her对象内的值了,通常用于初始化工做,例如jquery里的ajaxSetup也就是这种方式来实现的:编程
// 事先设置好初始值 $.ajaxSetup({ url: '/xmlhttp/', global: false, type: 'POST' }) // 而后再调用 $.ajax({ data:myData });
另外,不少jquery的插件也有这种形式的传参,只不过也能够不传,不传的时候则就使用默认值了。浏览器
返回函数,则是指一个函数的返回值为另外一个函数,或者根据特定的条件灵活建立的新函数,示例以下:闭包
var setup = function(){ console.log(1); return function(){ console.log(2); }; }; // 调用setup函数 var her = setup(); // 1 her(); // 2 // 或者直接调用也能够; setup()(); // 2
或者咱们能够利用闭包的特性,在函数中加入一个计数器,用来计算函数被调用的次数:
var setup = function(){ var count = 0; return function(){ retunr ++count; }; }; var next = serup(); next(); // 1 next(); // 2 next(); // 3
Currying是函数式编程的一个特性,将多个参数的处理转化成单个参数的处理,相似链式调用。
下面来举一个简单的例子:
function add(x, y){ var oldx = x, oldy = y; if(typeof oldy === 'undefined'){ return function (newy){ return oldx + newy; } } return x + y ; }
这样的话,调用方式就有不少种了,好比:
// 测试 typeof add(5); // 'function' add(3)(4); // 7 // 也能够这样调用 var add2000 = add(2000); add2000(10); // 2010
接下来咱们定义一个,比较经常使用的currying函数:
// 第一个参数为要应用的function, 第二个参数是须要传入的最少参数个数 function curry(func, minArgs) { if(minArgs == undefined) { minArgs = 1; } function funcWithArgsFrozen(frozenargs){ return function () { // 优化处理,若是调用时没有参数,返回该函数自己 var ages = Array.prototype.slice.call(arguments); var newArgs = frozenargs.concat(args); if(newArgs.length >= minArgs) { return func.apply(this, newArgs); }else{ return funcWithArgsFrozen(newArgs); } } } return funcWithArgsFrozen([]); }
这样,咱们就能够随意定义咱们的业务行为了,好比定义加法:
var plus = curry(function(){ var result = 0; for(var i=0; i<arguments.length; ++i){ result += arguments[i]; } return result; }, 2)
使用方法多种多样。
plus(3, 2) // 正常调用 plus(3) // 偏应用 plus(3) (2) // 完整应用(返回5) plus() (3) () () (2) // 返回5 plus(3, 2, 4, 5) // 能够接受多个参数 plus(3) (2, 3, 5) // 同理
JavaScript里的Function有不少特殊的功效,能够利用闭包以及arguments参数特性实现不少不一样的技巧,下一篇咱们将继续介绍利用Function进行初始化的技巧。
// 声明完函数之后,当即执行该函数 (function (){ console.log('watch out'); }()); // 这种方式声明的函数,也能够当即执行 !function(){ console.log('watch out!'); }(); // 以下的方式也均可以哦 ~function(){ /* do someing*/ }(); -function(){ /* do someing*/ }(); +function(){ /* do someing*/ }();
该模式的意思是指在声明一个对象(并不是函数)的时候、当即执行对象里的某一个方法进行初始化工做,一般该模式能够用在执行一次性的代码上:
({ // 这里你能够定义常量,并设置其它值 maxwidth: 600, maxheight: 400, // 固然也能够定义方法 gimmMax: function(){ return this.maxwidth + 'x' + this.maxheight; }, init: function(){ console.log(this.gimmeMax()); // 更多代码...... } }).init(); // 这样就能初始化喽
分支初始化是指在初始化的时候,根据不一样的条件(场景)初始化不一样的代码,也就是所谓的条件语句赋值。以前咱们在处理的时候,经常使用相似下面的代码:
var utils = { addListener: function(el, type, fn){ if(typeof window.addEventListener === 'function'){ el.addEventListener(type, fn, false); }else if(typeof document.attachEvent !== 'undefined'){ el.attachEvent('on' + type, fn); }else{ el['on' + type] = fn; } }, removeListener: function(el, type, fn){ // 神马之类的...... } }
咱们来改进一下,首先咱们要定义两个接口,一个用来add事件句柄,一个用来remove事件句柄,代码以下:
var utils = { addListener: null, removeListener: null };
实现代码以下:
if (typeof window.addEventListener === 'function') { utils.addListener = function (el, type, fn) { el.addEventListener(type, fn, false); }; } else if (typeof document.attachEvent !== 'undefined') { // IE utils.addListener = function (el, type, fn) { el.attachEvent('on' + type, fn); }; utils.removeListener = function (el, type, fn) { el.detachEvent('on' + type, fn); }; } else { // 其它旧浏览器 utils.addListener = function (el, type, fn) { el['on' + type] = fn; }; utils.removeListener = function (el, type, fn) { el['on' + type] = null; }; }
用起来,是否是就很方便了?代码也优雅多了。
通常是在函数内部,重写重名函数代码,好比:
var her = function(){ alert('Boo!'); her = function(){ alert('Double Boo!!'); } }
这种代码,很是容易令人迷惑,咱们先来看看例子的执行结果:
// 1.添加新属性 scareMe.prototype = 'Anna'; // 2. scareMe赋予一个新值 var prank = scareMe; // 3. 做为一个方法调用 var spooky = { boo: scareMe } // 使用新变量名称进行调用 prank(); // 'Boo!' prank(); // 'Boo!' console.log(spooky.boo.property); // 'properly'
经过执行结果,能够发现,将定于的函数赋值与新变量(或内部方法),代码并不执行重载的scareMe代码,而以下例子则正好相反:
// 使用自声明函数 scareMe(); // Double boo! scareMe(); // Double boo! console.log(scareMe.property); // undefined
你们使用这种模式时,必定要很是当心才行,不然实际结果极可能和你指望的结果不同,固然你也能够利用这个特殊作一些特殊的操做。
该模式主要是利用函数的属性特性来避免大量的重复计算。一般代码形式以下:
var myFunc = function (param) { if (!myFunc.cache[param]) { var result = {}; // ... 复杂操做 ... myFunc.cache[param] = result; } return myFunc.cache[param]; }; // cache 存储 myFunc.cache = {};
可是上述代码有个问题,若是传入的参数是toString或者其它相似Object拥有的一些公用方法的话,就会出现问题,这时候就须要使用传说中的hasOwnProperty
方法了,代码以下:
var myFunc = function (param) { if (!myFunc.cache.hasOwnProperty(param)) { var result = {}; // ... 复杂操做 ... myFunc.cache[param] = result; } return myFunc.cache[param]; }; // cache 存储 myFunc.cache = {};
或者若是你传入的参数是多个的话,能够将这些参数经过JSON的stringify方法生产一个cachekey值进行存储,代码以下:
var myFunc = function () { var cachekey = JSON.stringify(Array.prototype.slice.call(arguments)), result; if (!myFunc.cache[cachekey]) { result = {}; // ... 复杂操做 ... myFunc.cache[cachekey] = result; } return myFunc.cache[cachekey]; }; // cache 存储 myFunc.cache = {};
或者多个参数的话,也能够利用arguments.callee特性:
var myFunc = function (param) { var f = arguments.callee, result; if (!f.cache[param]) { result = {}; // ... 复杂操做 ... f.cache[param] = result; } return f.cache[param]; }; // cache 存储 myFunc.cache = {}; 总结