在SPA、各類MV*框架如日中天,先後端分離、工程化大行其道的現現在來看,能力檢測彷佛已經不那麼重要,由於大多產品已經再也不須要去兼容老舊的IE,或者說是更多偏向移動端的開發,雖然一些兼容的坑依舊存在,可是經過層出不窮的自動化工具,咱們已經沒必要事事躬親了。然而今天要向你們分享的則是關於事件兼容性的能力檢測,可能看上去沒什麼大的用場,倒是咱們理解惰性函數的好途徑。後端
先看一則常見的事件監聽封裝設計模式
function addEvent(ele, type, cb, isCap) { if (window.addEventListener) { ele.addEventListener(type, cb, isCap); } else if (window.attachEvent) { ele.attachEvent(type, function () { var args = [].prototype.slice(0); window.event[target] = window.event.srcElement; args.unshift(window.event); cb.apply(this, args); }); } else { ele['on' + type] = cb; } } var body = document.body; addEvent(body,'load',function () { console.log('onload') // onload }); addEvent(body,'click',function () { console.log('onclick') // onclick });
能夠看的到,這是一個有着足夠兼容性的的事件方法,可以應付常見的需求和場面。瀏覽器
只是細細想來,當咱們將這個方法複用了不少次後,會發現每次複用都會從新進行一次能力檢測,以便符合當前宿主環境。可能你會想,對於瀏覽器性能過剩的現現在來講,這點性能損耗並不算什麼。閉包
那麼不妨作個假設,當咱們在作一個高頻操做,例如window.onresize的監聽,鼠標隨便輕輕一拖,就可能會觸發無數次事件的重載。app
面臨這種情況,常規狀況下,有經驗的開發者大體會作兩種處理來改善性能。框架
- 函數節流,將方法限頻,過濾一些線性重複性的操做。這方面,在loadash和underscore都有相應的方法。往後我也會分享出原生的實現方式。
- 從源頭找起,把沒必要要的能力檢測去除,咱們只在頁面加載時檢測一次很差嗎?
函數節流咱們往後再分享,那麼,要如何把沒必要要的能力檢測去除掉呢?下面有兩種實現方式,代碼以下:前後端分離
//使用惰性函數 var addEvent = (function () { //當即執行 if (window.addEventListener) { return function (ele, type, cb, isCap) { ele.addEventListener(type, cb, isCap); } } else if (window.attachEvent) { return function (ele, type, cb) { ele.attachEvent(type, function () { var args = [].prototype.slice(0); window.event[target] = window.event.srcElement; args.unshift(window.event); cb.apply(this, args); }); } } else { return function (ele, type, cb) { ele['on' + type] = cb; } //初始判斷事後,addEvent就無需再次判斷,已是正確的封裝。 } }());
代碼變化並不大,關鍵點在於,咱們用到了當即執行函數,即方法在初始化完畢,便當即判斷,判斷後,返回適合當前場景的方法。函數
//常見的方法調用方式 function getName() { console.log('ives') } getName() // ives;
//當即執行 能夠是聲明函數 也能夠是匿名函數 兩種方式都可 (function getName() { console.log('ives') // ives }());
在常見的高階函數實踐中,咱們時常會接受函數,對函數的參數或者是上下文環境進行一些改造,並返回。在這種惰性判斷裏,咱們利用了函數一等公民的身份和當即執行的便利,進行了初始判斷,隨後經過return語句進行變量的重賦值,進而達到了性能上的提升。工具
實際上,不少經常使用的檢測,好比AJAX的封裝、獲取實際的CSS樣式等,均可以使用惰性函數處理。性能
然而,這種方法也並不是是完美的,咱們假設另一種場景,便是在靜態HTML中,咱們引用的某個JS文件中有這個方法,可是這個HTML頁面並無事件性質的交互,那麼咱們能夠基於吹毛求疵,雞蛋裏挑骨頭的角度來斷言,此次惰性加載是失敗的,無用的。由於咱們浪費了系統的資源,咱們沒有事件,你爲何要加載一次?
總而言之,惰性加載雖然性能有提高,卻仍然不是最好的處理,只是有利於咱們對於惰性函數的理解。
什麼情景纔是最完美的呢?便是初次調用時才作第一次判斷,隨後,無需判斷,方法也很簡單,只要簡單的改動幾行代碼。
依舊是以事件爲例,代碼以下:
var addEvent = function (ele,type,cb,isCap) { //取消了當即執行 初次調用時進行方法重載 if (window.addEventListener) { addEvent = function (ele, type, cb, isCap) { ele.addEventListener(type, cb, isCap); } } else if (window.attachEvent) { addEvent = function (ele, type, cb) { ele.attachEvent(type, function () { var args = [].prototype.slice(0); window.event[target] = window.event.srcElement; args.unshift(window.event); cb.apply(this, args); }); } } else { addEvent = function (ele, type, cb) { ele['on' + type] = cb; } } //初次調用時,手動執行重載的方法 addEvent(ele,type,cb,isCap); };
能夠看到實現的方式異常簡單,經過對方法的重載,並在初始執行時手動執行重載後的方法,知足了咱們上述的條件。
可能你們會說,你個標題黨,最後一種重載的方式纔是相對完美的方式,今天又爲何要講惰性函數呢?無外,拋磚引玉罷了,在鑽研設計模式時,是絕對離不開閉包和惰性函數的,今天則算是小小的熱身。