参与者模式

参与者模式

JavaScript中的参与者模式,就是在特定的作用域中执行给定的函数,并将参数原封不动的传递,参与者模式不属于一般定义的23种设计模式的范畴,而通常将其看作广义上的技巧型设计模式。

描述#

参与者模式实现的在特定的作用域中执行给定的函数,并将参数原封不动的传递,实质上包括函数绑定和函数柯里化。
对于函数绑定,它将函数以函数指针(函数名)的形式传递,使函数在被绑定的对象作用域中执行,因此函数的执行中可以顺利地访问到对象内部的数据,由于函数绑定构造复杂,执行时需要消耗更多的内存,因此执行速度上要稍慢一些,不过相对于解决的问题来说这种消耗还是值得的,因此它常用于事件,setTimeoutsetInterval等异步逻辑中的回调函数。

示例#

当我们需要为元素添加事件时,可以封装一个兼容的方法。

Copy
let A = { event: {} } A.event.on = function(dom, type, fn) { if(dom.addEventListener) { dom.addEventListener(type, fn, false); }else if(dom.attachEvent) { dom.attachEvent("on" + type, fn); }else{ dom["on"+ type] = fn; } }

这样对于事件绑定是完全可以的,但是我们不能传入一些参数以保持回调函数内部的词法作用域的数据的完整性,所以我们在回调函数中下点文章。

Copy
A.event.on = function(dom, type, fn, data) { if(dom.addEventListener) { dom.addEventListener(type, function(e){ fn.call(dom, e, data) }) } }

这样又导致了新问题,添加的事件回调函数不能移除了,所以还需要对其改进,使用bind改变绑定函数的this,实际上就是形成了一个闭包,现在bind普遍都在Function.prototype上实现了,但是在一些低版本浏览器还是需要自行实现,我们自行实现一个bind

Copy
function bind(fn, context) { return function() { return fn.apply(context, arguments) } }

尝试使用bind绑定this,接下来我们在事件绑定中就可以传递使用bind绑定的函数了。

Copy
var obj = { title: "test", } function test() { console.log(this.title) } var bindTest = bind(test, obj); bindTest(); // test

我们可以继续完善bindbind实际上是可以保存部分参数的,这时我们可以借助函数柯里化的思想将参数分开实现,当然直接使用ES6...操作符也是可以直接实现的。柯里化Currying是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术,是函数式编程应用。

Copy
// 柯里化可以参考 https://blog.touchczy.top/#/JavaScript/Js%E4%B8%ADCurrying%E7%9A%84%E5%BA%94%E7%94%A8 function bind(fn, context) { var slice = Array.prototype.slice; var args = slice.call(arguments, 2); return function() { var addArgs = slice.call(arguments); var allArgs = addArgs.concat(args); return fn.apply(context, allArgs); } } var obj = { title: "test", } function test(name) { console.log(this.title, name) } var bindTest = bind(test, obj, 1); bindTest(); // test 1
Copy
function bind(fn, context, ...args) { return function() { return fn.apply(context, args); } } var obj = { title: "test", } function test(name) { console.log(this.title, name) } var bindTest = bind(test, obj, 1); bindTest(); // test 1

实际上不光在事件绑定的时候我们使用到bind绑定作用域,在使用观察者模式时订阅的过程通常也是传递了一个绑定好的函数。

Copy
function PubSub() { this.handlers = {}; } PubSub.prototype = { constructor: PubSub, on: function(key, handler) { // 订阅 if(!(key in this.handlers)) this.handlers[key] = []; if(!this.handlers[key].includes(handler)) { this.handlers[key].push(handler); return true; } return false; }, once: function(key, handler) { // 一次性订阅 if(!(key in this.handlers)) this.handlers[key] = []; if(this.handlers[key].includes(handler)) return false; const onceHandler = (args) => { handler.apply(this, args); this.off(key, onceHandler); } this.handlers[key].push(onceHandler); return true; }, off: function(key, handler) { // 卸载 const index = this.handlers[key].findIndex(item => item === handler); if (index < 0) return false; if (this.handlers[key].length === 1) delete this.handlers[key]; else this.handlers[key].splice(index, 1); return true; }, commit: function(key, ...args) { // 触发 if (!this.handlers[key]) return false; console.log(key, "Execute"); this.handlers[key].forEach(handler => handler.apply(this, args)); return true; }, } const obj = { name: "me", say: function(){ console.log(this.name); } } const eventBus = new PubSub(); eventBus.on("say", obj.say); eventBus.on("say", obj.say.bind(obj)); eventBus.commit("say"); // say Execute // 这是触发这个事件的log // undefined // 未绑定this的触发 // me // 绑定this的触发

每日一题#

Copy
https://Github.com/WindrunnerMax/EveryDay

参考#

Copy
https://blog.csdn.net/zxl1990_ok/article/details/110095794 https://blog.csdn.net/Forever201295/article/details/104032369 https://stackoverflow.com/questions/2236747/what-is-the-use-of-the-javascript-bind-method
posted @   WindRunnerMax  阅读(198)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示
CONTENTS