今天看书自己写例子,碰到了一个问题。
由于 Array.every 是 ECMAScript 5 定义的方法,需要 IE 9+、FireFox 2+、Opera 9.5+、Safari 3+ 和 Chrome* 的浏览器版本支持,在考虑到兼容性的时候需要兼容性写法:
// 定义方式 1 var hs = { // 这是一个Array的工具类, 用以编写兼容性方法 ArrayUtil: { every: function(arr, func) { // 对参数的检测省略... if (Array.every) { // 优先使用原生方法 return arr.every(func); } else { // 否则使用兼容方法 var result = false; for(var i=0,len=arr.length; i
这样就可以实现对低版本浏览器(比如IE6)的兼容。
但这种定义方式使得 every 方法存在一个问题,就是它在每次被调用执行的时候都会进行重复的判断——浏览器是否支持原生的 Array.every?浏览器是否支持原生的 Array.every?浏览器是否支持原生的 Array.every?……哪怕得到的永远是同一个答案……
如果这个兼容性方法只是偶尔调用,那重复的判断也不是什么大问题。
但如果这个方法被频繁调用,重复的判断造成的性能浪费就不能忽视了。
于是就有了下面的定义方式:
// 定义方式 2 var hs = { ArrayUtil: { every: function() { // 这是一个自调函数, 在(对象)方法创建之处就进行兼容性判断, 只保留一个选择,这个选择就构成了 every 方法的全部内容。 if (Array.every) { return function(arr, func) { return arr.every(func); // 原来的执行代码被包裹在函数里, 而这个函数将被自调函数返回,并赋值给every。 }; } else { return function(arr, func) { var result = false; for(var i=0,len=arr.length; i
这样,仅有的一次判断只会在对象(方法)创建时进行一次,every 保留唯一一个方法,在被调用执行时不会再进行重复而无意义的判断。
这种方式使程序变得更简洁、运行也更快速(哪怕只是一点,但这是量的积累)。
然而,它也不是全无缺点的,它的问题体现在代码首次加载(或程序初始化)的时候:
如果是个人网站、轻量代码,它“一步到位”的优点会显露无疑;
但在面对门户网这类大型网站、在面对大量代码(需要进行大量兼容性判断)的时候,它的“一次性”会让你的程序执行显得拖沓和吃力。
所幸这问题也不是无法避免的,我们可以对程序进行“分流”处理:对程序里的兼容性方法还是选用一次性判断的方式,但这“一次性”不再扎堆于程序初始化,而是分流于方法初次调用时——在方法被明确调用的时候才进行唯一一次判断。
// 定义方式 3var hs = { ArrayUtil: { every: function(arr, func) { if (Array.every) { this.every = function(arr, func) { // 重新赋值 return arr.every(func); }; } else { this.every = function(arr, func) { // 重新赋值 var result = false; for(var i=0,len=arr.length; i
这就解决了问题,只在方法被用到的时候才进行一次判断,不影响对象构建、程序初始化的速度。
相比于第二种方式,这种方式在第一次调用方法时也会损失一些性能,但就整个程序的性能来说,这已经是最佳选择。
三种定义方式各有优缺点,不同使用环境:
兼容性方法偶尔使用,选方式 1
兼容性方法频繁使用,但量少,选方式 2
兼容性方法频繁使用,且量大,选方式 3
另外,在各种情况下都能表现良好和稳妥的,是方式 3