由于浏览器的差异,多数js代码包含大量if语句用来引导程序正确执行.
例子如下:
function createXHR() { if (typeof XMLHttpRequest != "undefined") { return new XMLHttpRequest(); //XMLHttpRequest类型不为undefined时返回XMLHttpRequest实例 } else if (typeof ActiveXObject != "undefined") { if (typeof arguments.callee.activeXString != "string") { var version = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp", ], i; for (i = 0, i < version.length; i++) { try { new ActiveXObject(version[i]); arguments.callee.activeXString = version[i]; break; } catch (ex) { // 跳过 } } } return new ActiveXObject(arguments.callee.activeXString); } else { throw new Error("NO XHR object available") } }
每次调用createXHR()时,它都会对浏览器支持的能力做检查;
首先检查内置的XHR,然后测试有没有基于ActiveX的XHR,两者都没有就抛出错误;
每次调用此函数都会这样,即使每次调用时分支的结果都不变,也都会执行if语句;
那如果浏览器支持内置XHR,也就意味着会一直支持,那么这个测试就没必要了,因为有if语句的函数要比没有if语句的慢;
所以,如果if语句不必每次都执行,那代码就运行的快一点,解决方案就是惰性载入的技巧.
表示函数执行的分支只会执行一次(只在执行分支代码时牺牲一点性能)
有两种方式实现惰性载入(这两种方式都能避免执行不必要的代码):
在第一次调用的过程中,改函数会被覆盖为另一个按合适方式执行的函数,这样任何对原函数的调用都不会再经过已经执行的分支了
例子重写如下:
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 version = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp", ], i; for (i = 0; i < version.length; i++) { try { new ActiveXObject(version[i]); arguments.callee.activeXString = version[i]; break; } catch (ex) { } } } return new ActiveXObject(arguments.callee.activeXString); } } else { createXHR = function () { throw new Error("NO XHL object available") }; } return createXHR(); }
在这个惰性载入的createXHR()中,if语句的每一个分支都会为createXHR变量赋值,从而覆盖了原有的函数,最后一句就是调用新赋的函数.下一次调用时,就会直接调用被分配的函数,这样就不用再次执行if语句了
这样在第一次调用函数时就不会损失性能了,而是在代码首次加载时会损失一点性能
例子重写如下:
var createXHR = (function () { if (typeof XMLHttpRequest != "undefined") { return function () { return new XMLHttpRequest() }; } else if (typeof ActiveXObject != "undefined") { return function () { if (typeof arguments.callee.activeXString != "string") { var version = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp", ], i; for (i = 0; i < version.length; i++) { try { new ActiveXObject(version[i]); arguments.callee.activeXString = version[i]; break; } catch (e) { // } } } return new ActiveXObject(arguments.callee.activeXString); }; } else { return function () { throw new Error("NO XHR object available") }; } })();
这个重写的例子中创建了一个匿名、自执行的函数,根据返回的结果来确定使用哪一个函数实现.实际逻辑是一样的,不一样的地方是第一行代码是使用var定义的函数,并且新增了自执行的匿名函数,每个分支都会返回正确的函数定义,以便将其赋值给createXHR().