如果一个函数中有不少局部变量,而且并非 primitive type 的,意味着在执行该函数的时候占用较多的内存开销。这一过程是显然易见的,例如执行这个函数 1000 次,就要重复创建那些局部变量 1000 次——这真是一个多余的过程。再者,如果函数逻辑不会去修改局部变量的值,即属于 constant / final 修饰的值,那么显然,我们仅仅创建一次变量便足够了。好,既然如此,我们把这些可以优化的变量都放置函数体外面好了。虽然 js 没有 constant / final 的修饰符,不过即使如此,那只会仅仅影响到程序的可读性这点不好。为优化这点,我们只需把变量的声明写到函数体外,不要写在函数里面。
有两点应注意:
- 你可选择不用局部变量,而是可以将变量写成 public 属性的调用方式,作用无异。但本文考虑的是基于替代局部变量的私有方式;
- 当然,这种优化不必然强制的,如果遇到是的简单的字符串,int……,那么小 case 写在函数体内倒无妨,解释器处理很快,再优化可能要差别不大。总之得看情况,千万别养成强迫症哦。
于是,我们需要考虑把这些 constant 放在何处合理的地方。通常会想到是一个闭包:
;(function(){
var regExp = /d/; // 提取变量声明语句
window.isNumber = function(v){
// var regExp = /d/;
return regExp.test(v);
}
})();
以上是一个简单的例子,就目的来说已经可以达到前面所说之要求了。我们利用一个闭包去存储私有变量,这些私有变量是可以被返回的函数访问的,但在这段代码外面则访问不到了。但问题是,若我们不想用闭包呢?那是一个“匿名函数”(反对使用闭包,其实也说不上究竟有什么不好,反正觉得多了一层“怪怪”的——纯属个人感观而言)。
这里,我们可以大胆使用“覆盖函数”的方式——竟然“覆盖”?是的,别以为“覆盖了就没了”是一件很危险的事哦:P,请注意我们这里是"巧用"覆盖。
以下就是一个例子:
/**
* 固定位置元素。el必须为绝对定位。
* @param {HTMLElement} el
* @param {Boolean} isOnTop 是否在最上方的,false=最下方
*/
$$.dhtml.fixedLayer = function(el, isOnTop){
var body = window.document.body;
var floor = window.Math.floor;
$$.dhtml.fixedLayer = function (el, isOnTop){
var lastScrollY = isOnTop ? -20 : -(window.innerHeight - el.clientHeight - 30); /* 调整 CSS Bottom 的值 */
window.setInterval(function(){
var percent = body.scrollTop - lastScrollY; // 移动的步伐是多大?
percent = shiftMove(percent);
if (percent == 0){
return; // 0表示不滚动,位置不变,所以DOM不作变化
}else{
lastScrollY += percent; // 保存偏移的位置,可正可负
addTop(el, percent);
}
}, 10);
}
/**
* stype.top 带单位的,运算时不方便,写一个函数处理吧
* @private
* @param {HTMLElement} el
* @param {Number} amount
* @return {Number} 增加 amount 后此时元素的 top 值
*/
function addTop(el, amount){
var top = window.parseInt(el.style.top) || 0; // style.top 有时为空字符,那就是 = 0
top += amount; // 弱类型的表现,先是int类型的+=
top += 'px'; // 然后这是 string 类型的!
el.style.top = top;
return top;
}
/**
* 为更加平滑,缩小移动的步伐。
* @private
* @param {Number} percent
* @return {Number}
*/
function shiftMove(percent){
percent = 0.2 * percent;
percent = floor(percent); // 取整数
return percent;
}
return $$.dhtml.fixedLayer(el, isOnTop);
}
这种的方式特点是创函数逻辑在函数内一层,第一次执行函数会覆盖原定义的函数(被覆盖的函数是外一层,完成了函数签名档作用)。函数自己覆盖掉自己,但函数名字依旧不变,所以这一切对外如何如何调用都是透明的。如果不调用方法,函数的逻辑并不执行,颇有点类 lazyExecute 意味。
对于 hash 对象的写法,也就是存在冒号 : 的写法,相应的处理如 lifesinger 所示:
createElement: function(sHtml) {
// ...
var createElement = function(sHtml) {
// ...
};
this.createElement = createElement;
return createElement(sHtml);
}
该模式的一个缺点是,如果要重命名函数名称,比不使用该模式多一次的操作。
实不相瞒,小弟当时也是受 lifesinger 在 zbm2001 博文之留言所启发而至的。源地址如下:
《自定义createElement——根据html字符串创建元素》http://zbm2001.iteye.com/blog/510627