四。惰性载入函数
因为浏览器兼容的原因,我们的javascript代码会有大量的if语句,将执行引导到正确的代码中,看如下函数:
function createXHR(){ if (typeof XMLHttpRequest != "undefined"){ return new XMLHttpRequest(); } else if (typeof ActiveXObject != "undefined"){ if (typeof arguments.callee.activeXString != "string"){ var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"], i, len; for (i=0,len=versions.length; i < len; i++){ try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; } catch (ex){ //skip } } } return new ActiveXObject(arguments.callee.activeXString); } else { throw new Error("No XHR object available."); } }
每次在调用createXHR时,都要对浏览器支持的能力进行检查。如果多次调用,这种检查就没有必要了。如果if语句不必每次执行,那么代码可以运行的更快一些。解决方案就是称之为惰性载入的技巧。
惰性载入表示函数执行的分支仅会发生一次。有两种实现惰性载入的方式。第一种就是在函数被调用时再处理函数:在第一次调用的过程中,该函数会被覆盖为另外一个按合适方式执行的函数,这样任何对原函数的调用都不用再经过执行分支。第二种是在声明函数时就指定适当的函数:这样第一次调用时就不会损失性能力,而在代码首次加载时会损失一点性能。具体看码~
1.在函数被调用时再处理函数
function createXHR(){ if (typeof XMLHttpRequest != "undefined"){ createXHR = function(){ return new XMLHttpRequest(); }; } else if (typeof ActiveXObject != "undefined"){ createXHR = function(){ if (typeof arguments.callee.activeXString != "string"){ var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"], i, len; for (i=0,len=versions.length; i < len; i++){ try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; } catch (ex){ //skip } } } return new ActiveXObject(arguments.callee.activeXString); }; } else { createXHR = function(){ throw new Error("No XHR object available."); }; } return createXHR(); } var xhr1 = createXHR(); var xhr2 = createXHR();
在第一次调用函数时会再处理函数,根据不同条件重写函数,这样在以后的调用中会直接调用新重写的函数而不用经过重重判断。
2.在声明函数时就指定适当的函数
1 var createXHR = (function(){ 2 if (typeof XMLHttpRequest != "undefined"){ 3 return function(){ 4 return new XMLHttpRequest(); 5 }; 6 } else if (typeof ActiveXObject != "undefined"){ 7 return function(){ 8 if (typeof arguments.callee.activeXString != "string"){ 9 var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", 10 "MSXML2.XMLHttp"], 11 i, len; 12 13 for (i=0,len=versions.length; i < len; i++){ 14 try { 15 new ActiveXObject(versions[i]); 16 arguments.callee.activeXString = versions[i]; 17 break; 18 } catch (ex){ 19 //skip 20 } 21 } 22 } 23 24 return new ActiveXObject(arguments.callee.activeXString); 25 }; 26 } else { 27 return function(){ 28 throw new Error("No XHR object available."); 29 }; 30 } 31 })(); 32 33 var xhr1 = createXHR(); 34 var xhr2 = createXHR();
//自执行匿名函数语法:(function(){})() 最开始看不懂这高大上的写法,原来是自执行函数~~~
这种方式类似于第一种,思路水样的,不同的就是第一行代码,新增了自执行的匿名函数,另外每个分支都返回正确的函数定义,以便立即将其复制给createXHR()。
五。函数柯里化
在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。这个技术由 Christopher Strachey 以逻辑学家哈斯凯尔·加里命名的,尽管它是 Moses Schönfinkel 和 Gottlob Frege 发明的。
这是来自维基百科的名词解释。顾名思义,柯里化其实本身是固定一个可以预期的参数,并返回一个特定的函数,处理批特定的需求。这增加了函数的适用性,但同时也降低了函数的适用范围。
理解起来比较困难,下面看实例:
function curry(fn){ var args = Array.prototype.slice.call(arguments, 1); //获取第一个参数之后的所有参数 return function(){ var innerArgs = Array.prototype.slice.call(arguments), //获取内部函数的所有参数 finalArgs = args.concat(innerArgs); //组合 return fn.apply(null, finalArgs); //没有考虑到执行环境,所以apply()第一个参数为null }; } function add(num1, num2){ return num1 + num2; } var curriedAdd = curry(add, 5); //创建了第一个参数绑定为5的柯里化版本 alert(curriedAdd(3)); //8 //传入第二个参数,调用add方法,相当于add(5,3) var curriedAdd2 = curry(add, 5, 12); alert(curriedAdd2()); //17
函数柯里化还常常作为函数绑定的一部分包含在其中,构造出更为复杂的bind()函数;
function bind(fn, context){ var args = Array.prototype.slice.call(arguments, 2); return function(){ var innerArgs = Array.prototype.slice.call(arguments), finalArgs = args.concat(innerArgs); return fn.apply(context, finalArgs); }; } var handler = { message: "Event handled", handleClick: function(name, event){ alert(this.message + ":" + name + ":" + event.type); } }; var btn = document.getElementById("my-btn"); EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler, "my-btn"));