【event】JS实现C# ‘+=‘ ‘-=‘ 事件委托

Github : esdelegates

先看效果

esdelegates

const del = delegates()                                    //创建事件分发器
const handler = () => { console.log('This is a handler') } //创建一个委托
del.propertyChanged += handler                             //添加委托到事件
del.propertyChanged()                                      //触发事件
// => This is a handler
del.propertyChanged -= handler                             //移除委托
del.propertyChanged()                                      //触发事件
// ...空

重载运算符?

JS是无法重载运算符的,除非修改解释器或者编译器替换模板。唯一能做的只有从现有的能够运算的预定义类型入手,这时候最合适的就是 number

valueOf

在进行 += / -= 运算的时候,会尝试计算对象的 valueOf 得到预定义类型,他们通常会返回自身然后再调用 toString 转化为字符串输出,如果要想实现

del.propertyChanged += function(){} 

就必须同时重写左右两者的 valueOf
右边很显然是 Function ,我们希望能够在计算之后依然能得到这个 Function ,而参与计算的是一个 number, 这就不得不使用下标

const globalDelegates = [] 								  //创建一个全局委托池
Function.prototype.valueOf = function(){ 				  //重写Function的valueOf	
	if(this.globalIndex != null) return this.globalIndex; //返回自身的全局索引
	const index = globalDelegates.length;                 //记录自己的下标
	Object.defineProperty(this, 'globalIndex', {          //写入自己的全局下标
		get(){ return index }
	})
	globalDelegates.push(this)                            //将自己添加到全局委托中
	return index
}

当创建一个委托之后进行计算

0 + () => {}   // => 0 代表他在全局委托池中的下标

那么左边应该是什么呢? 按照要求左边是能调用的事件,同时也应当是能够被传递的事件,那么也应当是一个方法。

del.propertyChanged() // 调用所有的委托

other.onClick += del.propertyChanged; //继续传递事件

delegates

现在来到最神秘的这个 del ,它产生的属性既可以调用也可以被追加事件,能做到的就是通过 get / set 分别写他的两种事件
对于 get 它应当返回一个可以被调用的函数,同时提供自己的事件下标

const handlers = {}
const callers = {}
	
get(target, p)
{
	handlers[p] ??= [] //被调用的时候初始化事件组
	return callers[p] ??= function (...args) //返回事件调用方
	{
		handlers[p]?.forEach(x => x(...args))
	}
},

对于 set 它应当接受一个 Function 或者一个 number

set(target, p, newValue)
{
	const event = handlers[p] ??= [];
	switch (typeof newValue)
	{
		case 'function':
			event.push(newValue)
			return true;
		case 'number':
			const eventIndex = callers[p].valueOf();
			let index = Math.abs(newValue - eventIndex);
			const delegate = globalDelegates[index];
			if (delegate == null) return false;
			if (newValue > eventIndex)
			{
				handlers[p].push(delegate)
				return true;
			}
			else
			{
				handlers[p].splice(handlers[p].findIndex(x => x === delegate), 1)
				return true;
			}
	}
	return false;
}

至此就实现了全部的效果

完整代码


const globalDelegates = []

Function.prototype.valueOf = function ()
{
	if (this.globalIndex !== undefined) return this.globalIndex
	const index = globalDelegates.length;
	Object.defineProperty(this, 'globalIndex', {
		get()
		{
			return index
		}
	})
	globalDelegates.push(this)
	return index
}

function delegates()
{
	const handlers = {}
	const callers = {}
	return new Proxy(
	{}, {
		get(target, p)
		{
			handlers[p] ??= []
			return callers[p] ??= function (...args)
			{
				handlers[p]?.forEach(x => x(...args))
			}
		},

		set(target, p, newValue)
		{
			const event = handlers[p] ??= [];
			switch (typeof newValue)
			{
				case 'function':
					event.push(newValue)
					return true;
				case 'number':
					const eventIndex = callers[p].valueOf();
					let index = Math.abs(newValue - eventIndex);
					const delegate = globalDelegates[index];
					if (delegate == null) return false;
					if (newValue > eventIndex)
					{
						handlers[p].push(delegate)
						return true;
					}
					else
					{
						handlers[p].splice(handlers[p].findIndex(x => x === delegate), 1)
						return true;
					}
			}
			return false;
		}
	})
}

// TESTS
const del = delegates()
const handler = () => { console.log('this is a handler') }
del.propertyChanged += handler;
del.onClick += del.propertyChanged
del.propertyChanged()
del.onClick()
del.propertyChanged -= handler;
del.propertyChanged()
  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值