The participant pattern in JavaScript refers to executing a given function within a specific scope and passing the parameters as-is. It does not belong to the category of the generally defined 23 design patterns, but it is usually regarded as a broad-spectrum technical design pattern.
The participant pattern involves executing a given function within a specific scope and passing the parameters as they are, fundamentally including function binding and function currying. When it comes to function binding, it involves passing the function as a function pointer (function name) so that the function can be executed within the scope of the bound object. As a result, the function can smoothly access the internal data of the object during execution. Since function binding is complex to construct and consumes more memory during execution, the speed of execution may be slightly slower. Nevertheless, considering the problems it addresses, this consumption is worthwhile. Thus, it is commonly used in events, setTimeout, or setInterval as asynchronous logic in callback functions.
In this way, event binding is completely possible, but we cannot pass in certain parameters to maintain the integrity of lexical scope data within the callback function. Therefore, we make some adjustments within the callback function.
A.event.on=function(dom, type, fn, data){if(dom.addEventListener){ dom.addEventListener(type,function(e){fn.call(dom, e, data)})}}
This leads to a new issue: the added event callback function cannot be removed. Hence, it needs further improvement by using bind to modify the binding function's this. In essence, it forms a closure. bind is now generally implemented on Function.prototype, but it still requires manual implementation in some older browsers. Let's implement a bind function manually:
Try using bind to bind this, and then we can pass the function bound with bind in event binding.
var obj ={title:"test",}functiontest(){ console.log(this.title)}var bindTest =bind(test, obj);bindTest();// test
We can further enhance bind. In fact, bind can save partial parameters. At this point, we can separate the parameters using the concept of function currying. Of course, directly using the ... operator in ES6 is also feasible. Currying is the technique of transforming a function that accepts multiple parameters into a function that accepts a single parameter and returns a new function that accepts the remaining parameters and returns the result, which is an application of functional programming.
// For reference on currying, please check: https://blog.touchczy.top/#/JavaScript/Js%E4%B8%ADCurrying%E7%9A%84%E5%BA%94%E7%94%A8functionbind(fn, context){var slice =Array.prototype.slice;var args =slice.call(arguments,2);returnfunction(){var addArgs =slice.call(arguments);var allArgs = addArgs.concat(args);returnfn.apply(context, allArgs);}}var obj ={title:"test",}functiontest(name){ console.log(this.title, name)}var bindTest =bind(test, obj,1);bindTest();// test 1
functionbind(fn, context,...args){returnfunction(){returnfn.apply(context, args);}}```javascript
var obj ={title:"test",}functiontest(name){ console.log(this.title, name)}var bindTest =bind(test, obj,1);bindTest();// test 1
Actually, not only do we use the bind to bind the scope when binding events, but also when subscribing in the process of using the observer pattern, we usually pass a bound function.
functionPubSub(){this.handlers ={};}PubSub.prototype ={constructor: PubSub,on:function(key, handler){// subscribeif(!(key inthis.handlers))this.handlers[key]=[];if(!this.handlers[key].includes(handler)){this.handlers[key].push(handler);returntrue;}returnfalse;},once:function(key, handler){// one-time subscriptionif(!(key inthis.handlers))this.handlers[key]=[];if(this.handlers[key].includes(handler))returnfalse;constonceHandler=(args)=>{handler.apply(this, args);this.off(key, onceHandler);}this.handlers[key].push(onceHandler);returntrue;},off:function(key, handler){// unloadconst index =this.handlers[key].findIndex(item=> item === handler);if(index <0)returnfalse;if(this.handlers[key].length ===1)deletethis.handlers[key];elsethis.handlers[key].splice(index,1);returntrue;},commit:function(key,...args){// triggerif(!this.handlers[key])returnfalse; console.log(key,"Execute");this.handlers[key].forEach(handler=>handler.apply(this, args));returntrue;},}const obj ={name:"me",say:function(){ console.log(this.name);}}const eventBus =newPubSub();eventBus.on("say", obj.say);eventBus.on("say", obj.say.bind(obj));eventBus.commit("say");// say Execute // This is the log for triggering this event// undefined // Unbound trigger// me // Bound trigger