函数是js世界的一等公民,js的动态性、易变性在函数的应用上,体现的淋漓尽致。做为参数,做为返回值等,正是函数这些特性,使得js开发变的有趣。
下面就阐述一下,js一个有趣的应用--惰性函数定义(Lazy Function Definition)。
惰性载入表示函数执行的分支只会在函数第一次掉用的时候执行,在第一次调用过程中,该函数会被覆盖为另一个按照合适方式执行的函数,这样任何对原函数的调用就不用再经过执行的分支了。
下面我们看几个典型的例子:
1 function addEvent (type, element, fun) { 2 if (element.addEventListener) { 3 element.addEventListener(type, fun, false); 4 } 5 else if(element.attachEvent){ 6 element.attachEvent('on' + type, fun); 7 } 8 else{ 9 element['on' + type] = fun; 10 } 11 }
上面是注册函数监听的各浏览器兼容函数。由于,各浏览之间的差异,不得不在用的时候做能力检测。显然,单从功能上讲,已经做到了兼容浏览器。
美中不足,每次绑定监听,都会对能力做一次检测。然而,真正的应用中,这显然是多余的,同一个应用环境中,其实只需要检测一次即可。
下面我们重写上面的addEvent:
1 function addEvent (type, element, fun) { 2 if (element.addEventListener) { 3 addEvent = function (type, element, fun) { 4 element.addEventListener(type, fun, false); 5 } 6 } 7 else if(element.attachEvent){ 8 addEvent = function (type, element, fun) { 9 element.attachEvent('on' + type, fun); 10 } 11 } 12 else{ 13 addEvent = function (type, element, fun) { 14 element['on' + type] = fun; 15 } 16 } 17 return addEvent(type, element, fun); 18 }
由上,第一次调用addEvent会对浏览器做能力检测,然后,重写了addEvent。下次再调用的时候,由于函数被重写,不会再做能力检测。
同样的应用,javascript高级程序设计里的一例子:
1 function createXHR(){ 2 if (typeof XMLHttpRequest != "undefined"){ 3 return new XMLHttpRequest(); 4 } else if (typeof ActiveXObject != "undefined"){ 5 if (typeof arguments.callee.activeXString != "string"){ 6 var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", 7 "MSXML2.XMLHttp"]; 8 9 for (var i=0,len=versions.length; i < len; i++){ 10 try { 11 var xhr = new ActiveXObject(versions[i]); 12 arguments.callee.activeXString = versions[i]; 13 return xhr; 14 } catch (ex){ 15 //skip 16 } 17 } 18 } 19 20 return new ActiveXObject(arguments.callee.activeXString); 21 } else { 22 throw new Error("No XHR object available."); 23 } 24 }
很显然,惰性函数在这里优势更加明显,因为这里有更多的分支。下面我们看一下重写后台的函数:
1 function createXHR() { 2 if (typeof XMLHttpRequest != "undefined") { 3 createXHR = function () { 4 return new XMLHttpRequest(); 5 } 6 return new XMLHttpRequest(); 7 } else if (typeof ActiveXObject != "undefined") { 8 var curxhr; 9 var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", 10 "MSXML2.XMLHttp"]; 11 12 for (var i = 0, len = versions.length; i < len; i++) { 13 try { 14 var xhr = new ActiveXObject(versions[i]); 15 curxhr = versions[i]; 16 createXHR = function () { 17 return new ActiveXObject(curxhr); 18 } 19 return xhr; 20 } catch (ex) { 21 //skip 22 } 23 } 24 } else { 25 throw new Error("No XHR object available."); 26 } 27 }
浏览器之间最大的差异,莫过于Dom操作,Dom操作也是前端应用 中最频繁的操作,前端的大多性能提升,均体现在Dom操作方面。
下面看一个Dom操作方面的惰性函数定义例子:
1 var getScrollY = function() { 2 3 if (typeof window.pageYOffset == 'number') { 4 5 getScrollY = function() { 6 return window.pageYOffset; 7 }; 8 9 } else if ((typeof document.compatMode == 'string') && 10 (document.compatMode.indexOf('CSS') >= 0) && 11 (document.documentElement) && 12 (typeof document.documentElement.scrollTop == 'number')) { 13 14 getScrollY = function() { 15 return document.documentElement.scrollTop; 16 }; 17 18 } else if ((document.body) && 19 (typeof document.body.scrollTop == 'number')) { 20 21 getScrollY = function() { 22 return document.body.scrollTop; 23 } 24 25 } else { 26 27 getScrollY = function() { 28 return NaN; 29 }; 30 31 } 32 33 return getScrollY(); 34 }
惰性函数定义应用还体现在创建单例上:
1 unction Universe() { 2 3 // 缓存的实例 4 var instance = this; 5 6 // 其它内容 7 this.start_time = 0; 8 this.bang = "Big"; 9 10 // 重写构造函数 11 Universe = function () { 12 return instance; 13 }; 14 }
当然,像上面这种例子有很多。惰性函数定义,应用场景我们可以总结一下:
1 应用频繁,如果只用一次,是体现不出它的优点出来的,用的次数越多,越能体现这种模式的优势所在;
2 固定不变,一次判定,在固定的应用环境中不会发生改变;
3 复杂的分支判断,没有差异性,不需要应用这种模式;