惰性载入函数

惰性载入函数由来

惰性载入函数的概念,最早见于《javascript高级程序设计》这本书;去年某个时候,自己偶然翻到了这一章;忽然感觉挺有道理的。最近呢,老是接触ajax这东东,我们知道浏览器之间行为的差异造成我们使用ajax,特别是创建XHR对象时,使用了大量的if判断,来做兼容性的处理。所以再次细细咀嚼了一下,写一篇博客分享再次强化。

常见的创建XHR对象的方式,类似如下代码:

//创建XHR对象
function createXHR() {
	if (typeof XMLHttpRequest != "undefined") {
		console.log("XMLHttpRequest,我被返回了一次");
		return new XMLHttpRequest();
	} else if (typeof ActiveXObject != "undefined") {
		if (typeof arguments.callee.activeXString != "string") {
			var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"];
			var length = versions.length;

			for (var i = 0; i < length; i++) {
				try {
					new ActiveXObject(versions[i]);
					arguments.callee.activeXString = versions[i];
					break;
				} catch (ex) {
					//todo something
				}

			}
		}
		console.log("ActiveXObject,我被返回了一次");
		return new ActiveXObject(arguments.callee.activeXString);
	} else {
		throw new Error("No XHR object available.");
	}
}

不知道,你有没有注意到,我们每次调用createXHR的时候,整个代码的逻辑判断都要走一遍直到浏览器支持某个对象,然后返回。(如果都没有就抛出一个错误)。

如下测试:

var xhr = createXHR();
var xhr2 = createXHR();
var xhr3 = createXHR();

结果如下图:

假如,一个应用里面调用了50,100次createXHR是不是就会按照上面的逻辑走50,100次。其实,我们应该这样想,如果浏览器支持,它就会一直支持;那么我们大量的if进行测试就显得没有必要,甚至多余了。从性能的角度来看,没有if条件判断肯定比含有if判断的快;

当然,我们上面的代码已经做了一次优化了。怎么说呢?

在上面代码中我们有意识的把判断XMLHttpRequest对象放在if首位;因为我们知道大部分浏览器支持XMLHttpRequest,自然也就进入if逻辑里面返回XMLHttpRequest,不必要接着做if判断。当然,如果你把XMLHttpRequest非首位,甚至放在最后,那就会增加if判断,自然性能比放在首位差一些。所以呢,在做浏览器功能检测时,我们常常把大部分浏览器支持的判断放在首位。

代码大概如下:

function isSupport(feature) {

	if () {//兼容大部分浏览器
		//todo something
	} else if () {//兼容少部分浏览器
		//todo something
	} else if() {//j极少数的浏览器
		//todo something
	} else {
		//todo something
	}
}

有点扯远了,我们回来。为了少进行if逻辑判断,甚至不进行或者进行一次;我们需要借助函数,来实现所谓的“惰性载入函数”。




惰性载入函数最佳实践(1

惰性载入,表示函数执行的分支进入发生一次。差不多有两种方式实现惰性载入。

在函数被调用时,再处理函数;什么意思呢,在第一次调用函数的过程中,该函数会被覆盖为另一个分支的处理函数,这样任何对函数的再次调用就不用再一次测试,判断,执行相关分支的代码。

上面创建XHR的方式,使用第一种“惰性载入”可以这样重写。

代码如下:

//第一种惰性载入的方式
function createXHR() {
	if (typeof XMLHttpRequest != "undefined") {
		console.log("XMLHttpRequest,我被返回了一次");
		createXHR = function () {
			return new XMLHttpRequest();
		}
	} else if (typeof ActiveXObject != "undefined") {
		console.log("ActiveXObject,我被返回了一次");
		createXHR = function() {
			if (typeof arguments.callee.activeXString != "string") {
				var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"];
				var length = versions.length;

				for (var i = 0; i < length; i++) {
					try {
						new ActiveXObject(versions[i]);
						arguments.callee.activeXString = versions[i];
						break;
					} catch (ex) {
						//todo something
					}

				}
			}

			return new ActiveXObject(arguments.callee.activeXString);
		}

	} else {
		createXHR = function() {
			throw new Error("No XHR object available."); 
		}

	}
	return createXHR();
}

上面代码,if的每一次分支都会为createXHR变量复制,重写;关键的最后一步,返回调用新复制的函数。这样下一次我们在调用createXHR时,就会直接调用被复制冲洗的函数,不用再次if判断返回了。

你可以尝试这样调用下一个。

代码如下:

var xhr = createXHR();
var xhr2 = createXHR();
var xhr3 = createXHR();

结果如下图:




惰性载入函数最佳实践(2

第二种实现惰性载入的方式是,声明函数时,就留用自执行函数返回指定的函数。这样旨在函数自执行是损失一点性能做功能检测;后面不管时,第一次被调用,还是第N次被调用就不用做功能检测,性能损失降到最低。

我们再次重写createXHR。

代码如下:

//第二种惰性载入的方式
var createXHR = (function() {
	if (typeof XMLHttpRequest != "undefined") {
		console.log("XMLHttpRequest,我被返回了一次");
		 return function () {
			return new XMLHttpRequest();
		}
	} else if (typeof ActiveXObject != "undefined") {
		console.log("ActiveXObject,我被返回了一次");
		return function() {
			if (typeof arguments.callee.activeXString != "string") {
				var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"];
				var length = versions.length;

				for (var i = 0; i < length; i++) {
					try {
						new ActiveXObject(versions[i]);
						arguments.callee.activeXString = versions[i];
						break;
					} catch (ex) {
						//todo something
					}

				}
			}
			return new ActiveXObject(arguments.callee.activeXString);
		}

	} else {
		return function() {
			throw new Error("No XHR object available."); 
		}
	}
})();

接着我们调用一下,代码如下:

var xhr = createXHR();
var xhr2 = createXHR();
var xhr3 = createXHR();

果真如预料的一样调用了三次都没有再执行if进行功能检测了。

好了,代码我就不多做解释了,我相信能静下心来看到这里的道友们,肯定已经很熟悉闭包,函数自执行等等了。



写在最后

“惰性载入”是继上一篇博客“作用域安全的构造函数”之后的有一个高级技巧。其优点是,只在首次执行代码时损失一点性能做功能检测。上面两种方式哪一种更适合你,看你而定。

时间: 2024-10-06 20:26:13

惰性载入函数的相关文章

javascript中惰性载入函数

我们都知道如果创建一个xhr对象(不会写,不怕,有百度,嘿嘿) function createXHR(){ if (typeof XMLHttpRequest != "undefined"){ return new XMLHttpRequest(); } else if (typeof ActiveXObject != "undefined"){ if (typeof arguments.callee.activeXString != "string&qu

JavaScript-编程技巧-惰性载入函数

var testFunc = function(){ if(typeof XMLHttpRequest != 'undefined'){ testFunc = function(){ alert('1'); } } else if(typeof ActiveXobject != 'undefined') { testFunc = function(){ alert('2'); } } else { testFunc = function(){ alert('3'); } } } 此函数在每次调用

js 性能优化整理之 惰性载入

跨检查浏览器特性,解决不同浏览器的兼容问题. 例如,我们最常见的为 dom 节点添加事件的函数 function addEvent(element,type,handler){ if(element.addEventListener){ element.addEventListener(type,handler,false) }else if(element.attacheEvent){ //attacheEvent element.attachEven('on'+type,function()

理解惰性函数

先列个通过if判断执行的代码块: creatXHR() function createXHR(){ if (typeof XMLHttpRequest != "undefined"){ // 首先检查内置的XHR return new XMLHttpRequest(); } else if (typeof ActiveXObject != "undefined"){ // 没有内置XHR就再测试有没有基于ActiveX的XHR if (typeof argument

javascript函数中的三个技巧【一】

在学习javascript中,函数是非常重要的,现在我来谈谈对函数的理解以及在工作和用法中的一些技巧 技巧一. [作用域安全的构造函数] 构造函数其实就是一个使用new操作调用的函数 function Person(name,age,job){ this.name=name; this.age=age; this.job=job; } var person=new Person('match',28,'Software Engineer'); console.log(person.name);/

javascript函数中的三个技巧【二】

技巧二: [惰性载入函数] 因为浏览器之间的行为的差异,我们经常会在函数中包含了大量的if语句,以检查浏览器特性,解决不同浏览器的兼容问题,比如,我们最常见的为dom节点添加时间的函数 function addEvent(type, element, fun) { if (element.addEventListener) { element.addEventListener(type, fun, false); } else if(element.attachEvent){ element.a

JavaScript设计模式与开发实践-读书笔记(3)闭包和高阶函数

闭包(closure) 闭包的形成与变量的作用域以及变量的生存周期密切相关. 变量的作用域,就是指变量的有效范围. 全局变量和局部变量. 在JavaScript中,函数可以用来创造函数作用域. 变量的生存周期,全局变量的生命周期是永久的,除非我们主动销毁这个全局变量. 对于在函数体内用var关键字声明的局部变量来说,当退出函数时,这些局部变量即失去了它们的价值,它们都会随着函数调用的结束而被销毁. 利用闭包我们可以完成许多奇妙的工作. 闭包的作用: 1.封转变量 闭包可以帮助我们把一些不需要暴露

JavaScript设计模式与开发实践——读书笔记1.高阶函数(下)

上部分主要介绍高阶函数的常见形式,本部分将着重介绍高阶函数的高级应用. 1.currying currying指的是函数柯里化,又称部分求值.一个currying的函数会先接受一些参数,但不立即求值,而是继续返回给另一个函数,通过闭包存储起来.等到函数被真正需求要求值的时候,将之前传入的参数统一起来求值.例如,我们要计算一个月的开销,我们并不需要计算每天具体花了多少,而是需要计算月底总共花掉多少,也就是说,实际上我们只需要在月底计算一次.所以每个月的前29天,我们都只需要保存好当天的开销,到30

高性能封装检测浏览器支持css3属性函数

css3出来已经很久了,现在来谈判断浏览器是否支持某个css3的属性虽说有点过时了,但是还是可以谈谈的,然后,此篇主要谈的不是判断是否支持,而是怎么封装更好,为什么这么封装,欢迎吐槽. 入题,判断浏览器是否支持css3 transition,方法很简单,只需要下面这句代码就行了: 'transition' in document.body.style chrome和ie支持document.body,但是Firefox不支持,Firefox支持document.documentElement,对