刘念的个人博客

0%

利用函数的惰性载入提高javascript代码性能

假装有个需求

我们现在需要写一个 foo 函数,这个函数返回首次调用时的 Date 对象,注意是首次

普通方法

1
2
3
4
5
6
var t;
function foo() {
if (t) return t;
t = new Date()
return t;
}

问题有两个,一是污染了全局变量,二是每次调用 foo 的时候都需要进行一次判断。

首先解决污染全局变量的问题

  1. 闭包

    1
    2
    3
    4
    5
    6
    7
    8
    var foo = (function() {
    var t;
    return function() {
    if (t) return t;
    t = new Date();
    return t;
    }
    })();
  2. 函数对象

    1
    2
    3
    4
    5
    6
    //函数也是一种对象,利用这个特性,我们也可以解决这个问题。
    function foo() {
    if (foo.t) return foo.t;
    foo.t = new Date();
    return foo.t;
    }

不过依旧没有解决调用时都必须进行一次判断的问题。

解决的方案就是称之为惰性载入的技巧。

​ 所谓惰性载入,就是说函数的if分支只会执行一次,之后调用函数时,直接进入所支持的分支代码。 有两种实现惰性载入的方式,第一种事函数在第一次调用时,对函数本身进行二次处理,该函数会被覆盖为符合分支条件的函数,这样对原函数的调用就不用再经过执行的分支了, 我们可以用下面的方式使用惰性载入重写 foo().

1
2
3
4
5
6
7
var foo = function() {
var t = new Date();
foo = function() {
return t;
};
return foo();
};

常见应用举例

在DOM 事件添加中,为了兼容现代浏览器和万恶的 IE 浏览器,我们需要对浏览器环境进行一次判断:

1
2
3
4
5
6
7
8
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 语句不必每次都执行,代码可以运行的更快一些。

1
2
3
4
5
6
7
8
9
10
11
12
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 语句了。

​ 上面这种方法会在函数第一次调用时损失一点性能,有可能造成用户第一次使用功能时产生卡顿.使用闭包可以解决这个问题.

1
2
3
4
5
6
7
8
9
10
11
12
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);
}
}
})();

​ 虽然利用闭包在第一次调用函数时就不会损失性能了,但是会在代码加载时会损失一点性能。所以只能具体情况具体分析了.