Javascript Custom Event Explanation

  • 2021-07-10 18:50:20
  • OfStack

Javascript custom events, its essence is observer mode (also known as subscription/publication mode), its advantage is that binding events and triggering events are isolated from each other, and events can be dynamically added and deleted.

Let's build a concrete Javascript custom event object step by step through an example.

For example: I have an action1 function, and I want to trigger another service1 function every time I finish executing action1, so we can write the code as follows:


// Services service1
function service1(){

}
// Function action1
function action1(){
 //other things
 //then  Start service1
 service1();
}

Good, but now the idea has changed. I want to trigger not only service1, but also service2 and service3 after action1 is completed.

Following the train of thought just now, after the function action1 is completed, just add them incidentally.

As follows:


function service1(){}
function service2(){}
function service3(){}

function action1(){
 //other things 
 service1();
 service2();
 service3();
}

However, my thoughts fluctuated again. After executing the action1 function, I suddenly wanted to dynamically add an service4, and found that service2 seemed meaningless. I didn't want to trigger it. What should I do?

You might say, wouldn't it be over to remove service2 and then add service4 after action1?

However, with the increasing number of real project development codes, it is difficult to find relevant codes to operate.

What can I do?

Initial idea, define an array (such as: servicearray) to manage all service.

When action1 is executed to the end, traverse the array function (servicearray) once, and it will be oak.

And if we don't want to run service2, just delete it from this array; If you want to add a new service, just append it to the servicearray array.

So nice, as follows:


var servicearray = [];

function service1(){}
function service2(){}
function service3(){}
// Will all service Add to servicearray Medium 
servicearray.push(service1);
servicearray.push(service2);
servicearray.push(service3);
//del Used to delete 1 Specified service
function del(arr, fn){
 for(var i = 0; i < arr.length; i++){
  if( arr[i] == fn ){
   arr.splice(i,1);
   break;
  }
 } 
}
//action1 After, execute all the service
function action1(){
 //other things 
 // Traversal serviceaary , execute all service Function. ( servicearray In action1 Inside) 
 for(var i =0; i < servicearray.length; i++){
  servicearray[i]();
 }
}
// Add service4
function service4(){}
servicearray.push(service4);
// Delete service2
del(servicearray, service2);

The above code is quite oak, but the reusability is not strong at 1 point, and servicearray and action have me and I have you, which is not good. Let's optimize it again.

The code is as follows:


var servicearray = [];

function service1(){}
function service2(){}
function service3(){}

servicearray.push(service1);
servicearray.push(service2);
servicearray.push(service3);
function del(arr, fn){
 for(var i = 0; i < arr.length; i++){
  if( arr[i] == fn ){
   arr.splice(i,1);
   break;
  }
 } 
}
// Add 1 A service4
function service4(){}
servicearray.push(service4);
// Delete 1 A service2
del(servicearray, service2);
// Add 1 Trigger function hanldeAction , separation action And service
function hanldeAction(actionName,serviceArr){
 if(typeof actionName === 'function'){
  actionName();
  for(var i =0; i < serviceArr.length; i++){
   serviceArr[i]();
  }
 }
}
// Execute 
handleAction(action1,servicearray); 

The above code is similar to the callback function, because what we want to achieve is to run series 1 service after action execution is completed.

However, I have changed my mind now. I want to execute the 1 series service before the action is executed, or in the action. It seems that we have to change the hanldeAction callback function, which is modified repeatedly in the project, obviously not.

Therefore, we have to make it stronger. (I can execute it wherever I want it to.)

As follows:


function service1(){}
function service2(){}
function service3(){}

var servicearray = [];
servicearray.push(service1);
servicearray.push(service2);
servicearray.push(service3);
function del(arr, fn){
 for(var i = 0; i < arr.length; i++){
  if( arr[i] == fn ){
   arr.splice(i,1);
   break;
  }
 } 
}
// Add 1 A service4
function service4(){}
servicearray.push(service4);
// Delete 1 A service2
del(servicearray, service2);
/*
 actionObj Used to store all action And service The object associated with the. 
  Such as: {
   action1:[service1,service2],
   action2:[...]
  }
*/
var actionObj = {};
/*
  Modify the code and add 1 A actionName And serviceArr Associate events. 
  For example, action1 Associate all service , and then combine the following trigger The event will be perfect 
 Params:
   actionName --> actionObj Properties of 
   serviceArr -->  Contains all associated with actionName Relative service Array 
*/
function addAction(actionName, serviceArr){
 if(typeof actionObj[actionName] === 'undefined' ){
  actionObj[actionName] = [];
 }
 if(typeof serviceArr === 'object'){
  actionObj[actionName].push(serviceArr);
 }
}
/*
  Modify the code and add 1 Trigger actionName Events 
  For example, when I want to trigger action1 All in service Call the trigger(action1) Just OK La 
*/
function trigger( actionName ){
 var act = actionObj[actionName];
 if(act instanceof Array){
  for(var i = 0, len = act.length; i < len; i++){
   for(var j =0, arrlen = act[i].length; j++){
    ((act[i])[j])();
   }
  }
 }
}

The above code is OK, but there is a performance problem. What is added to actionObj [actionName] in addAction is an array. In fact, the defined servicearray array (the array declared for storing different service) can be completely removed, and each service can be directly push into the array declared by actionObj [actionName], so the efficiency of trigger events has also been improved, from the original two-layer for cycle to the one-layer for cycle. In addition, we add another method remove to delete service.

The collation code is as follows:


var actionObj = {};
// Modify the code and add 1 A actionName And service Function directly associates events 
function addAction(actionName, fn){
 if(typeof actionObj[actionName] === 'undefined' ){
  actionObj[actionName] = [];
 }
 if(typeof fn === 'function'){
  actionObj[actionName].push(fn);
 }
}
// Modify the code and add 1 Trigger actionName Events 
function trigger( actionName ){
 var actionarray = actionObj[actionName];
 if(actionarray instanceof Array){
  for(var i = 0, len = actionarray.length; i < len; i++){
   if(typeof actionarray[i] === 'function'){
    actionarray[i]();
   }
  }
 }
}
// Modify the code and add 1 Delete actionName In service Events 
function remove(actionName, fn){
 var actionarray = actionObj[actionName];
 if(typeof actionName === 'string' && actionarray instanceof Array){
  if(typeof fn === 'function'){
   // Clear actionName Corresponding to the fn Method 
   for(var i=0, len = actionarray.length; i < len; i++){
    if(actionarray[i] === fn){
     actionObj[actionName].splice(i,1);
    }
   }
  }
 }
}

The above code is good, and action and service do not affect each other, and fulfill its mission.

Mission?

This is the first custom event we wrote. Is it very simple?

Hahaha, I also use design pattern (observer pattern) in my code.

1 drum up gas, let's optimize the above code again. Have you noticed that we are using global variables? In the environment of modular development, we are actually using global variables, which is not polluting the namespace. Change it again.

The modified code is as follows:


var EventTarget = function(){
 this.listener = {};
}
EventTarget.prototype = {
 constructor:EventTarget,
 addAction: function(actionName, fn){
  if(typeof actionName === 'string' && typeof fn === 'function'){
   // If it doesn't exist actionName , just create a new 1 A 
   if(typeof this.listener[actionName] === 'undefined'){
    this.listener[actionName] = [fn];
   }
   // Otherwise, go directly to the corresponding actinoName Inside plug 
   else{
    this.listener[actionName].push(fn);
   }
  }
 },
 trigger: function(actionName){
  var actionArray = this.listener[actionName];
  // Trigger 1 Series actionName Function in 
  if(actionArray instanceof Array){
   for(var i = 0, len = actionArray.length; i < len; i++){
    if(typeof actionArray[i] === 'function'){
     actionArray[i]();
    }
   } 
  }
  actionArray = null;
 },
 remove: function(actionName, fn){
  var actionArray = this.listener[actionName];
  if(typeof actionName === 'string' && actionArray instanceof Array){
   if(typeof fn === 'function'){
    // Clear actionName Corresponding to the fn Method 
    for(var i=0, len = actionArray.length; i < len; i++){
     if(actionArray[i] === fn){
      this.listener[actionName].splice(i,1);
     }
    }
   }
  }
  actionArray = null;
 }
};

1 JavaScript custom event is newly released.


Related articles: