代码优化--惰性载入函数

        有一句话说的好,没有if的语句,比有if的语句,拥有更高的性能,虽然,这种性能的差距,是非常小的,还有一句话呢,说的更好,不积跬步,无以至千里,不积小流,无以成江河,所以呢,如果可以大规模的减小这种if的使用,不就可以提升一些性能了吗?

        所以呢,就出现了惰性载入函数的技术。它的原理其实很简单,浏览器之间都是存在差距的,我们就需要用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中,本来是有自己的属性的,那么用这个方法继承之后,就不在存在自己的属性了,其实和原型链继承中的断链有些类似。所以要多多注意。



         就这样吧,感谢查看,欢迎交流,谢谢!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值