假装有个需求
我们现在需要写一个 foo 函数,这个函数返回首次调用时的 Date 对象,注意是首次。
普通方法
var t;
function foo() {
if (t) return t;
t = new Date()
return t;
}
问题有两个,一是污染了全局变量,二是每次调用 foo 的时候都需要进行一次判断。
首先解决污染全局变量的问题
-
闭包
var foo = (function() {
var t;
return function() {
if (t) return t;
t = new Date();
return t;
}
})();
-
函数对象
//函数也是一种对象,利用这个特性,我们也可以解决这个问题。 function foo() { if (foo.t) return foo.t; foo.t = new Date(); return foo.t; }
不过依旧没有解决调用时都必须进行一次判断的问题。
解决的方案就是称之为惰性载入的技巧。
所谓惰性载入,就是说函数的if分支只会执行一次,之后调用函数时,直接进入所支持的分支代码。 有两种实现惰性载入的方式,第一种事函数在第一次调用时,对函数本身进行二次处理,该函数会被覆盖为符合分支条件的函数,这样对原函数的调用就不用再经过执行的分支了, 我们可以用下面的方式使用惰性载入重写 foo()
.
var foo = function() {
var t = new Date();
foo = function() {
return t;
};
return foo();
};
常见应用举例
在DOM 事件添加中,为了兼容现代浏览器和万恶的 IE 浏览器,我们需要对浏览器环境进行一次判断:
function addEvent (type, el, fn) {
if (window.addEventListener) {
el.addEventListener(type, fn, false);
}
else if(window.attachEvent){
el.attachEvent('on' + type, fn);
}
}
每次调用 addEvent
函数的时候,它都要对浏览器所支持的能力进行检查,首先检查是否支持 addEventListener
方法,如果不支持,再检查是否支持 attachEvent
方法。 这个过程在 addEvent
函数每次调用的时候都要走一遍,其实,如果浏览器支持其中的一种方法,那么他就会一直支持了,就没有必要再进行其他分支的检测了, 也就是说,if
语句不必每次都执行,代码可以运行的更快一些。
function addEvent (type, el, fn) {
if (window.addEventListener) {
addEvent = function (type, el, fn) {
el.addEventListener(type, fn, false);
}
}
else if(window.attachEvent){
addEvent = function (type, el, fn) {
el.attachEvent('on' + type, fn);
}
}
}
在这个惰性载入的 addEvent()
中,if
语句的每个分支都会为 addEvent
变量赋值,有效覆盖了原函数。 最后一步便是调用了新赋函数。下一次调用 addEvent()
的时候,便会直接调用新赋值的函数,这样就不用再执行 if
语句了。
上面这种方法会在函数第一次调用时损失一点性能,有可能造成用户第一次使用功能时产生卡顿.使用闭包可以解决这个问题.
var addEvent = (function(){
if (window.addEventListener) {
return function (type, el, fn) {
el.addEventListener(type, fn, false);
}
}
else if(window.attachEvent){
return function (type, el, fn) {
el.attachEvent('on' + type, fn);
}
}
})();
虽然利用闭包在第一次调用函数时就不会损失性能了,但是会在代码加载时会损失一点性能。所以只能具体情况具体分析了.