原文:http://dean.edwards.name/weblog/2009/03/callbacks-vs-events/
先看如下代码:
1 document.addEventListener("DOMContentLoaded", function() {
2 console.log("Init: 1");
3 DOES_NOT_EXIST++; // error
4 }, false);
5
6 document.addEventListener("DOMContentLoaded", function() {
7 console.log("Init: 2");
8 }, false);
你预期当页面加载后,console下会出现什么结果?
结果是这样的:
Init: 1Uncaught ReferenceError: DOES_NOT_EXIST is not defined
Init: 2
重点在于: 两个事件监听函数都执行了.虽然在第一个事件监听函数中出现了错误,但并没有阻止第二个函数的执行.
问题来了.
接下来我们基于回调函数系统的代码.使用jQuery:
1 $(document).ready(function() {
2 console.log("Init: 1");
3 DOES_NOT_EXIST++; // error
4 });
5
6 $(document).ready(function() {
7 console.log("Init: 2");
8 });
此时你从console下看到了什么?没错,是这样:
Init: 1
Uncaught ReferenceError: DOES_NOT_EXIST is not defined
好吧,这意味着回调函数系统是极其脆弱的.一旦任何一个回调函数中抛出了异常,则余下的回调函数序列将不再执行.
在实际开发环境中,这意味着一个写得烂的插件可以令其他插件无法初始化.
Dojo与jQuery有相同的问题,而YUI包装了try/catch机制,它会让回调函数中的错误悄悄地被捕获:
1 YAHOO.util.Event.onDOMReady(function() {
2 console.log("Init: 1");
3 DOES_NOT_EXIST++; // this will throw an error
4 });
5
6 YAHOO.util.Event.onDOMReady(function() {
7 console.log("Init: 2");
8 });
所以你将在console看到如下结果:
Init: 1Init: 2
几近完美的初始化! 貌似没什么好担心的了,除了那些你看不到的错误.
那该如何解决呢?
下面的解决方案是这样的: 使用回调函数混合真正的事件调度.
我们可以触发一个自定义事件,并在该事件的监听函数中,迂回地执行回调函数.
因为每个事件处理程序都有它自己的上下文,所以,即便在事件处理函数内发生了错误,也不会影响到我们的回调函数系统了.
回调函数序列中的每一个函数都将被执行.
这里是代码:
1 var currentHandler;
2
3 if (document.addEventListener) {
4 document.addEventListener("fakeEvents", function() {
5 // execute the callback
6 currentHandler();
7 }, false);
8
9 var dispatchFakeEvent = function() {
10 var fakeEvent = document.createEvent("UIEvents");
11 fakeEvent.initEvent("fakeEvents", false, false);
12 document.dispatchEvent(fakeEvent);
13 };
14 } else { // MSIE
15
16 document.documentElement.fakeEvents = 0; // an expando property
17
18 document.documentElement.attachEvent("onpropertychange", function(event) {
19 if (event.propertyName == "fakeEvents") {
20 // execute the callback
21 currentHandler();
22 }
23 });
24
25 dispatchFakeEvent = function(handler) {
26 // fire the propertychange event
27 document.documentElement.fakeEvents++;
28 };
29 }
30
31 var onLoadHandlers = [];
32 function addOnLoad(handler) {
33 onLoadHandlers.push(handler);
34 };
35
36 window.onload = function() {
37 for (var i = 0; i < onLoadHandlers.length; i++) {
38 currentHandler = onLoadHandlers[i];
39 dispatchFakeEvent();
40 }
41 };
这次,执行结果当然又是我们预期的了:
Init: 1Uncaught ReferenceError: DOES_NOT_EXIST is not defined
Init: 2
回调函数与DOM事件
时间: 2024-11-08 13:14:02