关于惰性载入函数

有一句话说的好,没有if的语句,比有if的语句,拥有更高的性能,虽说差距很小,但是古人曾说:不积跬步,无以至千里,不积小流,无以成江河。再代码的世界里,这无疑是一个真理,所以,如何能更好的提高代码性能,是每一个开发者需要关注的核心技能,是一个开发者向更高层次迈进的一种象征。

概述

提升代码性能的方法有很多,这里不对这些方法做列表式的总结,单独的说一下惰性载入函数对于代码性能提升的一些原理。

惰性载入函数

为什么会有惰性载入函数这个技术,那么首先要想到的一点就是,作为一个前端工程师,首先要解决的一个问题,就是浏览器的兼容性问题,一种刘爱兰器支持哪些属性,包含哪些方法,都是固定的,所以,针对于浏览器,当页面加载完毕之后,支持的方法也就固定了,接下来,就拿事件绑定的API进行后面的说明。

为了简便,封装了一个函数名为addEvent,来处理不同浏览器下,事件绑定方法的兼容性问题,于是,实现为如下方法:


function addEvent(obj,type,fn){
    if(obj.addEventListener){
        obj.addEventListener(type,fn,false);
    }else if(obj.attachEvent){
	obj.attachEvent("on"+type,fn);
    }else{
	obj["on"+type] = fn;
    }
}


不管代码还是逻辑,看起来都是没有什么问题的,有功能性验证,保证各浏览器都可以正确的绑定事件,于是可以如下的方法,给document添加一个点击事件:


addEvent(document,"click",function(){alert("alert");});


看起来很是OK。

但是呢,想一个问题,如果我再当前的页面中,需要绑定100个事件,1000个事件呢,那么我就需要使用到相同数量的addEvent方法,而这个时候,我就要执行更多数量次的if-else的判断,可是,当我页面加载完毕的时候,浏览器已经是一个固定的浏览器了,我只需要一次判定,就能确定该浏览器支持的事件绑定方法是addEventListener还是attachEvent了,而不需要再以后每次都进行判断。

所以,为了解决这个问题,就有了惰性载入函数的思想,惰性载入的实现,可以分为两种方法,接下来会对每一种进行解析说明。

初始化JS时处理

这种写法最简单的方式是,初始化为不同的函数,那么就可以如下的方法实现:


var addEvent = null;

if(document.addEventListener){
    addEvent = function(obj,type,fn){
	obj.addEventListener(type,fn,false);
    };
}else if(document.attachEvent){
    addEvent = function(obj,type,fn){
	obj.attachEvent("on"+type,fn);
    }
}else{
    addEvent = function(obj,type,fn){
	obj["on"+type] = fn;
    }
}


使用方法与之前的相同,这里之所以使用函数表达式的形式来做这个判断,主要是基于两个原因:即JS没有块级作用域和JS函数没有重载。

所以,如果我如下的写法:


if(document.addEventListener){
    function addEvent(obj,type,fn){
	alert("addEventListener");
	obj.addEventListener(type,fn,false);
    };
}else if(document.attachEvent){
    function addEvent(obj,type,fn){
	alert("attachEvent");
	obj.attachEvent("on"+type,fn);
    }
}else{
    function addEvent(obj,type,fn){
	alert("on"+type);
	obj["on"+type] = fn;
    }
}

addEvent(document,"click",function(){alert("alert");});
//这个时候,当页面加载完毕,调用addEvent方法添加事件时,会弹出什么信息?
//因为function会预解析,而JS中又没有块级作用域,并且JS函数不能重载。


继续说,上面的写法虽然达到了我们想要的,可是却不是最好的,因为这些都直接写在全局作用域中的,为了能更好的模块化,所以把上述方法,以自执行函数的形式实现,所以可以修改为如下:


var addEvent = (function(){
    if(document.addEventListener){
	return function (obj,type,fn){
	    obj.addEventListener(type,fn,false);
	};
    }else if(document.attachEvent){
	return function(obj,type,fn){
	    obj.attachEvent("on"+type,fn);
	}
    }else{
	return function(obj,type,fn){
	    obj["on"+type] = fn;
	}
    }
})();


这就是惰性载入函数的一种形式,看起来是不是很面熟?因为这也是属于闭包的一种应用。

该中写法的惰性载入的优势在于,只有在页面加载时,会进行一次功能性验证,只有这个时候,会消耗一点点性能,而在之后再使用addEvent时,就不会再次进行验证了。

这种写法的惰性加载就说道这里,下面看另外一种。

首次运行时处理

不管是那种写法,归根究底,都是对同名函数进行重定义,而这里要说的惰性加载方法,就是在首次调用该方法时,才会去重新定义该函数。

而之前的初始化时惰性加载方法,是在初始化时,就重新定义对应的函数了。

所以,这里的实现方法可以如下实现:


function addEvent(obj,type,fn){
    console.log("asdad");
    //这里打印一个数据,用来表示该方法执行一次之后,即被重载。
    if(obj.addEventListener){
	addEvent = function(obj,type,fn){obj.addEventListener(type,fn,false);}
    }else if(obj.attachEvent){
	addEvent = function(obj,type,fn){obj.attachEvent("on"+type,fn);}
    }else{
	addEvent = function(obj,type,fn){obj["on"+type] = fn;}
    }
	
    addEvent(obj,type,fn);
}
addEvent(document,"click",function(){alert("alert");});
addEvent(document,"click",function(){alert("alert");});
addEvent(document,"click",function(){alert("alert");});


其实,这里也是闭包的一种,看下面的例子:


function addEvent(obj,type,fn){
    var firstType = type;
    //闭包内,保存一个初始化数据
    if(obj.addEventListener){
	addEvent = function(obj,type,fn){
	    alert("firstType="+firstType);
	    obj.addEventListener(type,fn,false);
	}
    }else if(obj.attachEvent){
	addEvent = function(obj,type,fn){
	    alert("firstType="+firstType);
	    obj.attachEvent("on"+type,fn);
	}
    }else{
	addEvent = function(obj,type,fn){
	    alert("firstType="+firstType);
	    obj["on"+type] = fn;
	}
    }
	
    addEvent(obj,type,fn);
}
addEvent(document,"dblclick",function(){alert("alert");});
addEvent(document,"click",function(){alert("alert");});
addEvent(document,"click",function(){alert("alert");});


OK,不做更多的解释。

个人观点

这两种惰性载入的方法,我个人更喜欢第二种,原因:
1:两种写法,都只有一次功能判断和函数重定义。
2:第一种在初始化时重定义的缺点在于,它在没有用到该属性前,就消耗了代码的性能,也许,最终也不会用到该功能,所以当首次使用时,再做重定义,可以避免页面加载时,就损失性能的问题。

惰性加载在一些插件中,都有使用,只是让我很疑惑的是,jQuery作为一个使用广泛的框架,竟然没有使用~~

总结

惰性载入,其实质也是闭包的一种应用。

在刚开始写本篇文章时,想到了好多要写的东西,可是,写到最后,有些要写的东西,却想不起来了,到这里,还总是感觉有些东西想写,却没有写的感觉。

也许是与惰性载入相关的一些概念,如果您知道,请指教!

本文地址:http://www.zhangyunling.com/?p=375

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>