国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

最佳的addEvent事件綁定是怎樣誕生的

2019-11-20 23:37:03
字體:
供稿:網(wǎng)友

當我們編寫腳本的時候創(chuàng)建了交叉引用,例如如下代碼:

window.onload = function() {  var x = document.getElementsByTagName('H3');  for (var i = 0; i < x.length; i++) {    x[i].onclick = openClose;    x[i].relatedElement = x[i].nextSibling; // simplified situation     x[i].relatedElement.relatedElement = x[i];  }}

或者在函數(shù)中使用腳本語言最常見的閉句Closures的時候,IE都無法回收內(nèi)存。而閉句在給DOM對象注冊事件處理器(event handler)的時候最為常用。Novemberborn提供了一些example可以讓你運行并切實感受到這個bug。
我最喜愛的QuirkMode 去年初意識到這個bug存在巨大隱患,覺得有必要呼吁廣大web開發(fā)者關(guān)注并竭力避免這個問題,于是舉辦了一個慈善邀請賽,鼓勵大家提交各自 addEvent/removeEvent 方案。并終于在去年10月下旬宣布了他們認為的勝利者:John Resig,讓John贏得勝利的代碼如下:

function addEvent(obj, type, fn) {  if (obj.attachEvent) {    obj['e' + type + fn] = fn;    obj[type + fn] = function() {      obj['e' + type + fn](window.event);    }    obj.attachEvent('on' + type, obj[type + fn]);  } else obj.addEventListener(type, fn, false);}function removeEvent(obj, type, fn) {  if (obj.detachEvent) {    obj.detachEvent('on' + type, obj[type + fn]);    obj[type + fn] = null;  } else obj.removeEventListener(type, fn, false);}

QuirkMode 對選擇John為勝利者的解釋概括來說就是以上代碼最簡潔有效,在避免內(nèi)存問題的同時還巧妙的保證了this關(guān)鍵字在ie的attachEvent中能正常工作。缺點當然還是存在:

不支持 Netscape 4 和 Explorer 5 Mac。(有可能國內(nèi)的程序員會嗤之以鼻,但國外很強調(diào)廣泛的兼容性)
在 removeEvent 中遺漏了remove obj["e"+type+fn]。
總之不管怎么說,簡單取勝。
結(jié)果一出,眾多參賽與評論者不服氣,很快又挑出了John的代碼的幾處毛病:

addEvent中本身就使用了閉句,所以沒有根本解決IE內(nèi)存泄露的問題。
沒有解決同類型的事件可能被重復注冊而被IE重復執(zhí)行的問題。
幾個高手于是提出了改進性的方案:

/* Original idea by John Resig Tweaked by Scott Andrew LePera, Dean Edwards and Peter-Paul Koch Fixed for IE by Tino Zijdel (crisp) Note that in IE this will cause memory leaks and still doesn't quite function the same as in browsers that do support the W3C event model: - event execution order is not the same (LIFO in IE against FIFO) - functions attached to the same event on the same element multiple times will also get executed multiple times in IE */function addEvent(obj, type, fn) {  if (obj.addEventListener) obj.addEventListener(type, fn, false);  else if (obj.attachEvent) {    obj["e" + type + fn] = fn;    obj.attachEvent("on" + type,    function() {      obj["e" + type + fn]();    });  }}function removeEvent(obj, type, fn) {  if (obj.removeEventListener) obj.removeEventListener(type, fn, false);  else if (obj.detachEvent) {    obj.detachEvent("on" + type, obj["e" + type + fn]);    obj["e" + type + fn] = null;  }}

很明顯,雖然修正了John代碼的一些不足。但內(nèi)存泄露依然存在,部分瀏覽器依然不支持,還是無法避免ie重復注冊。另外根據(jù)注釋:當在同一個對象上注冊多個事件處理器的時候,IE與其他瀏覽器的執(zhí)行順序是不同的,這又是一個隱患。

幾天之后,一個被認為最嚴謹?shù)姆桨赣蒁ean Edwards 提出。Dean他的方案與眾不同:

不執(zhí)行對象檢測(Object detection)
沒有調(diào)用 addeventListener/attachEvent 方法
保持this關(guān)鍵字的運行于正確的上下文環(huán)境
正確傳遞 event 對象參數(shù)
完全跨瀏覽器至此(包括IE4和NS4)
不存在內(nèi)存泄露

Dean的代碼如下:

// written by Dean Edwards, 2005 // http://dean.edwards.name/function ;addEvent(element, type, handler) { // assign each event handler a unique ID // 為事件處理函數(shù)設(shè)定一個唯一值 if (!handler.$$guid) handler.$$guid = addEvent.guid++;// create a hash table of event types for the element if (!element.events) element.events = {};// create a hash table of event handlers for each element/event pair var handlers = element.events[type];if (!handlers) {  handlers = element.events[type] = {};  // store the existing event handler (if there is one)   // 如果對象已經(jīng)注冊有事件處理,那么要保留下來,并保存為第一個   if (element["on" + type]) {    handlers[0] = element["on" + type];  }}// store the event handler in the hash table handlers[handler.$$guid] = handler;// assign a global event handler to do all the work // 指派一個全局函數(shù)做統(tǒng)一的事件處理,同時避免了反復注冊 element["on" + type] = handleEvent;};// a counter used to create unique IDs addEvent.guid = 1;function removeEvent(element, type, handler) {  // delete the event handler from the hash table   if (element.events && element.events[type]) {    delete element.events[type][handler.$$guid];  }};function handleEvent(event) {  // grab the event object (IE uses a global event object)   event = event || window.event;  // get a reference to the hash table of event handlers   // 這里的 this 隨 handlerEvent function 被觸發(fā)的source element 變化而變化   var handlers = this.events[event.type];  // execute each event handler   for (var i in handlers) {    //這樣寫才能保證注冊的事件處理函數(shù)中的 this 得到正確的引用,直接handlers[i]()是不行的     this.$$handleEvent = handlers[i];    this.$$handleEvent(event);  }};

這段代碼相比之前就大了不少了,不過確實很精妙。可是這段代碼卻引入了其他的問題,比如無法處理事件處理函數(shù)的返回值,for..in循環(huán)可能因為 (Object.prototype)的錯誤應用而中斷等等...很快Dean推出一個"updated version"。

要做到最好真的好辛苦。

目前似乎Dean的最終版本是最全面的解決方案。不過就我個人意見,感覺有些吹毛求疵了。盡量使用瀏覽器本身的實現(xiàn)和保持簡單是我一貫堅持的主張。但洋人這種嚴謹?shù)膽B(tài)度,還是讓我深深敬佩。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 镶黄旗| 垣曲县| 莱阳市| 石棉县| 新巴尔虎左旗| 永春县| 长寿区| 交城县| 祁门县| 平阴县| 体育| 通江县| 当雄县| 合江县| 女性| 阳高县| 城市| 灵丘县| 同仁县| 卓尼县| 阳信县| 中西区| 体育| 雅安市| 乐安县| 上林县| 洪江市| 博兴县| 浮山县| 石门县| 宝丰县| 托里县| 鹿邑县| 容城县| 梁河县| 门源| 土默特左旗| 徐汇区| 西充县| 酒泉市| 祥云县|