十、JavaScript专题之惰性函数

什么是惰性函数

惰性函数表示函数执行的分支只会在函数第一次调用的时候执行,在第一次调用过程中,该函数会被覆盖为另一个按照合适方式执行的函数,这样任何对原函数的调用就不用再经过执行的分支了。本文将详细介绍惰性函数

使用背景

我们知道 javascript 最大的问题就是浏览器的兼容问题,一个 api 在不同的浏览器调用的结果是不一样的,有的时候我们需要通过 if 判断来实现不同的兼容问题:

1
2
3
4
5
6
7
8
9
function addEvent(type, element, fun) {
if (element.addEventListener) {
element.addEventListener(type, fun, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, fun);
} else {
element["on" + type] = fun;
}
}

上面是一个兼容不同浏览器事件绑定的方法,但是,他有一个问题:

每次调用 addEvent 函数的时候都要进行 if 判断,如果浏览器支持其中的一个方法,那么它就永远支持了,没有必要再进行其他分支的检测了。也就是说,if 语句不必每次都执行,代码可以运行的更快一些,解决方案就是惰性载入。

函数重写

在介绍惰性函数之前,首先介绍函数重写技术。由于一个函数可以返回另一个函数,因此可以用新的函数来覆盖旧的函数。

1
2
3
4
5
6
7
8
function a() {
console.log("a");
a = function () {
console.log("b");
};
}
a(); // a
a(); // b

第一次调用该函数时会 console.log(‘a’)会被执行;然后全局变量 a 被重定义,并被赋予新的函数。当该函数再次被调用时, console.log(‘b’)会被执行;

惰性函数

惰性函数的本质就是函数重写。所谓惰性载入,指函数执行的分支只会发生一次,有两种实现惰性载入的方式:

1、第一种是在函数被调用时,再处理函数。函数在第一次调用时,该函数会被覆盖为另外一个按合适方式执行的函数,这样任何对原函数的调用都不用再经过执行的分支了。代码重写如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function addEvent(type, element, fun) {
if (element.addEventListener) {
addEvent = function (type, element, fun) {
element.addEventListener(type, fun, false);
};
} else if (element.attachEvent) {
addEvent = function (type, element, fun) {
element.attachEvent("on" + type, fun);
};
} else {
addEvent = function (type, element, fun) {
element["on" + type] = fun;
};
}
return addEvent(type, element, fun);
}

在这个惰性载入的 addEvent()中,if 语句的每个分支都会为 addEvent 变量赋值,有效覆盖了原函数。最后一步便是调用了新赋函数。下一次调用 addEvent()时,便会直接调用新赋值的函数,这样就不用再执行 if 语句了。
但是,这种方法有个缺点,如果函数名称有所改变,修改起来比较麻烦。

2、第二种是声明函数时就指定适当的函数。把嗅探浏览器的操作提前到代码加载的时候,在代码加载的时候就立刻进行一次判断,以便让 addEvent 返回一个包裹了正确逻辑的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var addEvent = (function () {
if (document.addEventListener) {
return function (type, element, fun) {
element.addEventListener(type, fun, false);
};
} else if (document.attachEvent) {
return function (type, element, fun) {
element.attachEvent("on" + type, fun);
};
} else {
return function (type, element, fun) {
element["on" + type] = fun;
};
}
})();

综上,当我们每次都需要进行条件判断,其实只需要判断一次,接下来的使用方式都不会发生改变的时候,想想是否可以考虑使用惰性函数。

参考文献