如果一個函數中有不少局部變數,而且並非 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