从我的博客:
随着您的代码库变得越来越难以阅读和维护,使用计数器和其他不合标准的方法将鼓励代码功能障碍。实现这一点的理想方法是使用事件驱动的方法。它更容易维护,并导致更少的意大利面条代码。你可以很容易地实现这样的事情:
//First, we need an object that will contain our 'event domain'
var EventDomain = new function(config,callback){
this.__listeners = {}; //this will store references to functions associated to events
this.on = function(event,listener){ //here, we provide a member that binds
//a 'listener' function to an event string
if (typeof this.__listeners[event] == "undefined"){
this.__listeners[event] = [];
}
this.__listeners[event].push(listener);
};
this.emit = function(event,params){ //here, we provide a member that 'emits' an
//event string, calling any 'listener' functions
//and passing a parameter object
//{param1: val1, param2:val2, paramN, valN}
if(typeof event == "string"){
event = { type: event };
}
if(!event.target){
event.target = this;
}
if(!event.type){
throw new Error("Event object missing 'type' property.");
}
if(this.__listeners[event.type] instanceof Array){
var listeners = this.__listeners[event.type];
for (var i=0, len=listeners.length; i < len; i++){
listeners[i](params);
}
}
};
this.removeListener = function(type, listener){ //here, we provide a member that allows
//us to remove a 'listener' function
//from an event
if(this.__listeners[type] instanceof Array){
var listeners = this.__listeners[type];
for (var i=0, len=listeners.length; i < len; i++){
if (listeners[i] === listener){
listeners.splice(i, 1);
break;
}
}
}
};
this.removeAllListeners = function(type){ //here, we provide a member that allows
//us to remove ALL 'listener' functions
//from an event
if(this.__listeners[type] instanceof Array){
delete this.__listeners[type];
}
};
};
现在我们有了一个事件域,我们可以将它与上面的代码片段一起使用。但首先,让我们创建一个对象来充当我们的 ajax 调用的控制器:
var AjaxController = new function(){
var counter; //create a private member called counter
this.onLoad = function(fn){ //here, we provide a member to specify what to do
//when ajax requests begin
EventEmitter.on('ajax_loading',(function(fn){ //bind 'ajax_loading' event to an
//ajax loading indicator
return function(){ //yet another closure
if(counter<1){ //obviously, we only want to do something on the first
//load, not every single time. Otherwise, you'll end
//end up trying to show the exact same loading animation
//for each ajax call
fn();
}
counter++;
};
})(fn));
};
this.onComplete = function(fn){ //here we provide a member to specify what to do
//when ALL ajax requests have finished
EventEmitter.on('ajax_complete',(function(fn){ //bind the 'ajax_complete' event
//to a loading indicator
return function(){ //yet another closure
counter--; //decrement concurrent loading
if(counter<1){ //its only finished once the counter is 0!
fn();
}
}
})(fn));
};
var constructor = function(){ //good object oriented programming uses constructors!
counter = 0;
//Now, lets overload the original $.ajax method to provide functionality
//for our event driven approach
$.ajax = (function(ajax){ //this is called a 'closure'.
//We pass it the original function and it wraps and
//returns it, overloading the original method
function overloadAjax(params){ //this is our overloading function
if(params.success){ //if there is a success parameter passed
//to the ajax call, overload that as well
//to emit the 'ajax_complete' event
params.success = (function(success){
function overloadSuccess(data){
EventDomain.emit('ajax_complete');
success(data);
}
return overloadSuccess; //return and overload success
})(params.success);
}
EventDomain.emit('ajax_loading'); //emit the 'ajax_loading' event
//for each ajax call
ajax(params);
}
return overloadAjax; //here we return 'overload' back to $.ajax, overloading
//the original
})($.ajax);
}
constructor(); //we call the constructor after all members have been prototyped
}
现在我们已经为自己创建了一个很好的、可重用的 ajax 加载器工具包,让我们使用它:
AjaxController.onLoad(function(){
//put some code here to do DOM manipulation to add a loader, or however you
//want to 'indicate' ajax loading
});
AjaxController.onComplete(function(){
//put some code here to do some DOM manipulation to remove your loader, or however
//you want to 'indicate' that ajax loading is complete
});
现在,它只是工作:
$("#search_building").on("change blur", function () {
var building = $("#search_building").val();
var room = $("#search_room").val();
var dept = $("#dept").val();
var dataString = 'room=' + room + '&' + 'building=' + building + '&' + 'dept=' + dept;
$.ajax({
type: "POST",
url: "process_building.php",
data: dataString,
cache: false,
success: function (html) {
$('#search_room').html(html);
}
});
$.ajax({
type: "POST",
url: "process_timetableMon.php",
data: dataString,
cache: false,
success: function (html) {
$('#grid2_mon').html(html);
}
});
$.ajax({
type: "POST",
url: "process_timetableTue.php",
data: dataString,
cache: false,
success: function (html) {
$('#grid2_tue').html(html);
}
});
$.ajax({
type: "POST",
url: "process_timetableWed.php",
data: dataString,
cache: false,
success: function (html) {
$('#grid2_wed').html(html);
}
});
$.ajax({
type: "POST",
url: "process_timetableFri.php",
data: dataString,
cache: false,
success: function (html) {
$('#grid2_wed').html(html);
}
});
$.ajax({
type: "POST",
url: "process_roomList.php",
data: dataString,
cache: false,
success: function (html) {
$('#list2').html(html);
}
});
});
在前端将事件驱动编程与面向对象编程相结合的巨大好处是,您最终可以让您的开发人员腾出更多的时间来进行更多的实际内容创建和更少的问题解决。创建一个可重用的框架需要更多的时间开始,但在未来会有指数级的时间/质量优势。如果您想在这些概念方面获得更多帮助,请随时给我发电子邮件:rhyneandrew@gmail.com。我很乐意免费为您提供帮助;)