惰性载入函数

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

使用背景

因为各浏览器之间的行为的差异,经常会在函数中包含了大量的 if 语句,以检查浏览器特性,解决不同浏览器的兼容问题。比如,最常见的为 dom 节点添加事件的函数

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 函数的时候,它都要对浏览器所支持的能力进行检查,首先检查是否支持 addEventListener 方法,如果不支持,再检查是否支持 attachEvent 方法,如果还不支持,就用 dom0 级的方法添加事件

这个过程,在 addEvent 函数每次调用的时候都要走一遍,其实,如果浏览器支持其中的一种方法,那么它就会一直支持了,就没有必要再进行其他分支的检测了。也就是说,if 语句不必每次都执行,代码可以运行的更快一些

解决方案就是惰性载入

函数重写

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

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

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

惰性函数

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

  • 第一种是在函数被调用时,再处理函数。函数在第一次调用时,该函数会被覆盖为另外一个按合适方式执行的函数,这样任何对原函数的调用都不用再经过执行的分支了。代码重写如下
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 语句了
但是,这种方法有个缺点,如果函数名称有所改变,修改起来比较麻烦

  • 第二种是声明函数时就指定适当的函数。把嗅探浏览器的操作提前到代码加载的时候,在代码加载的时候就立刻进行一次判断,以便让 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;
};
}
})();

惰性函数应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function createXHR() {
var xhr = null,
flag = false,
ary = [
function() {
return new XMLHttpRequest();
},
function() {
return new ActiveXObject("Microsoft.XMLHTTP");
},
function() {
return new ActiveXObject("Msxml2.XMLHTTP");
},
function() {
return new ActiveXObject("Msxml3.XMLHTTP");
}
];
//循环遍历这个数组,看看数组内的哪一个元素兼容浏览器,如果兼容,就赋值给给xhr,并且返回出去
for (var i = 0, len = ary.length; i < len; i++) {
var curFn = ary[i];
try {
xhr = curFn();
createXHR = curFn; //重新定义createXHR ,第二次调用ajax请求,则直接使用这个方法
flag = true;
break;
} catch (e) {
//说明浏览器不兼容,继续循环
}
}
if (!flag) {
throw new Error("你的浏览器不支持 ,请升级浏览器");
}
return xhr;
}