所以呢,就出现了惰性载入函数的技术。它的原理其实很简单,浏览器之间都是存在差距的,我们就需要用if语句来解决不同浏览器之间的兼容问题。
就拿继承这个事情来说吧,写一个继承的函数,实现一个对象继承另外一个对象的功能,当然啦,这里就不考虑原型链的继承了,原型链的继承,还是有挺多内容需要考虑的,可以参考之前写的一点东西:原型链断链的情况,原型的继承。
代码:
function extend(sub,sup){
//sup 继承 sub
for(var i in sub){
sup[i] = sub[i];
}
}
没有什么问题(这里只是最简单的实现,只是在为了说明惰性载入函数呢,所以,不要因为这个忽略很多非法操作,鄙视我啊!!!),但是呢,在ECMAScript5中,Object中,有了一个create方法,根据我们平常的理解,js中,内置的方法,拥有更好的性能,所以呢,我想要在支持ECMAScript5的浏览器中,使用create方法,所以,我把上述函数进行了这样的修改。
function extend(sub,sup){
//sup 继承 sub;
if("create" in Object){
return sup = Object.create(sub);
}else{
for(var i in sub){
sup[i] = sub[i];
}
return sup;
}
}
看起来还不错,可是,如果在我的代码中,会很频繁的用到这个方法呢,那么每次执行这个方法时,都会去进行if判断,而我这个时候,使用的浏览器,是已经固定的了,所以这种每次都需要的判断,是非必要的。并且,即使只有一个if语句,也肯定要比没有if语句的代码执行要慢(虽然差距不大),为了更优的代码,我想要去掉这个,所以,就有了惰性载入函数的思想--惰性载入函数的主要思想,就是在函数内部重写函数,并且覆盖它本身,覆盖之后,再次调用的时候,就没有if分支的存在了。
function extend(sub,sup){
//sup 继承 sub;
if("create" in Object){
//contain create
extend = function(sub,sup){
return sup = Object.create(sub);
};
}else{
//No create in Object
extend = function(sub,sup){
for(var i in sub){
sup[i] = sub[i];
}
return sup;
}
}
return extend(sub,sup);
}
实现的方法,有两种:第一种,函数被调用时处理:比如看代码吧
function extend(sub,sup){
//sup 继承 sub;
if("create" in Object){
//contain create
extend = function(sub,sup){
return sup = Object.create(sub);
};
}else{
//No create in Object
extend = function(sub,sup){
for(var i in sub){
sup[i] = sub[i];
}
return sup;
}
}
return extend(sub,sup);
}
上面这个写法呢,只有在第一次调用的时候,才会出现if语句的判断,在之后的调用,extend的内容是已经变了,支持ECMAScript5的就会是
extend = function(sub,sup){
return sup = Object.create(sub);
}
不支持的呢,就会是
extend = function(sub,sup){
for(var i in sub){
sup[i] = sub[i];
}
return sup;
}
那么,在其他代码中,如果再次需要extend的时候,就可以直接使用覆盖之后的逻辑了,不需要用到if语句了。
至于为何最后有个return extend(sup,sub);这个其实才是在执行第一次调用。上面那些只是在重写extend方法,如果不加这个return,那么第一次调用该方法去实现继承是无效的。不可丢。
第二种方法,在文件加载的时候,就会执行。这个时候呢,写法是要稍微改一下的,要改成赋值的那种情况。并且返回值也要修改的,这里需要把新的逻辑模块返回。代码如下:
var extend = (function(sub,sup){
//sup 继承 sub;
if("create" in Object){
return extend = function(sub,sup){
return sup = Object.create(sub);
};
}else{
return extend = function(sub,sup){
for(var i in sub){
sup[i] = sub[i];
}
return sup;
}
}
})();
这两种方法,各有特点:
1:使用该方法的位置,第一种随时可以使用,第二种,必须在执行过这个代码之后,才可以使用。
2:性能损失,第一种在第一次使用时才会有一点损失,第二种在加载的时候,会损失,如果有些是不一定会被用到的,还是第一种好些吧。
3:想了下,没有想到,等到添加......
惰性载入函数到这里就算告一段落,下面就上面的继承的代码做个补充。
用一下刚封装好的继承吧,是不是会发现一个问题,构造函数的继承在支持ECMAScript5的浏览器中,没有成功,发现了吧,原因是,Object.create这个方法,是不能用作构造函数的继承的。那么这个我要用在哪里呢?
var person = {
name:"zhang",
sayName:function(){
return this.name;
}
};
var myPerson = {
};
如果,此时我要myPerson继承person,如何做呢?我现在呢,之前我只知道一种方法,就是用for in去循环(这其实也是最不错的方法吧,构造函数的继承也可以用这个方法),现在呢,再加一种方法,就是Object.create方法了,试试看
myPerson = Object.create(person);
这样就继承了,并且myPerson中继承来的方法,就算是有修改,也不会影响到远person中的方法和属性。
例如:
myPerson.name = "lin";
console.log(myPerson.name); //lin
console.log(person.name); //zhang
这个方法的实质,其实和forin相同,就是在新方法里面,逐个添加,只是这个方法是浏览器内置封装好的。
至于其他的继承,比如构造函数的继承(一般使用的原型链继承),比较复杂,这里就不叙述了。
注:补充2013.12.17,上面的性能检测方法有点二了,利用 ‘ in ’去判断,虽然可以判断create方法是否存在于Object对象中,但是在这里,我必须要求create是一个function方法,所以这里这样进行能力判断,就没有达到想要的效果。可以改成
if("create" in Object && Object.prototype.toString.call(Object.create) == "[Function object]")
或者,因为此处的特殊情况,直接写成:
if(Object.prototype.toString.call(Object.create) == "[Function object]")
这个至于怎么写,就随意吧。
而且,这个方法,或许我们可以重写一下,把不支持create的浏览器,给Object添加一个这样的方法。比如可以写成这样:
if(!("create" in Object)){
Object.prototype.create = function(sub){
if(typeof sub == "object"){
for(var i in sub){
this[i] = sub[i];
}
}
return this;
}
}
好吧,其实有句话说的好,不要轻易修改对象,尤其是原生的对象。这里,你可以当我没有说吧。
不过,这个create方法使用时要注意一点,如果myPerson要继承person对象,那么myPerson对象中的其他属性会被丢掉,如果myPerson中,本来是有自己的属性的,那么用这个方法继承之后,就不在存在自己的属性了,其实和原型链继承中的断链有些类似。所以要多多注意。