Skip to content

Instantly share code, notes, and snippets.

@hax
Created September 1, 2022 10:57
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hax/9e7a010f5467fdd5b203479eb33e94e3 to your computer and use it in GitHub Desktop.
Save hax/9e7a010f5467fdd5b203479eb33e94e3 to your computer and use it in GitHub Desktop.
JS trick to mimic C# obj.onevent += delegate
const HandlerID = Symbol()
export function defineEvent(obj, name) {
const handlers = new Set()
const fn = function () {
for (const handler of handlers) {
try {
handler()
} catch (e) {
Promise.reject(e)
}
}
}
fn[HandlerID] = 0
Object.defineProperty(obj, name, {
get() { return handlers.size ? fn : null },
set(v) {
if (v === null) {
// console.log('=', v)
handlers.clear()
} else if (typeof v == "function") {
// console.log('=', v)
handlers.clear()
handlers.add(v)
} else if (v > 0) {
// console.log('+=', v)
v = handlerMaps.get(v)
if (!v) throw new TypeError()
handlers.add(v)
} else if (v < 0) {
// console.log('-=', v)
v = handlerMaps.get(-v)
if (!v) throw new TypeError()
handlers.delete(v)
} else {
throw new TypeError()
}
},
})
}
const handlerMaps = new Map()
Function.prototype.valueOf = function () {
return this[HandlerID] ?? genHandlerID(this)
}
function genHandlerID(fn) {
Promise.resolve(fn).then(disposeHandlerID)
let id
do { id = Math.random() }
while (handlerMaps.has(id))
handlerMaps.set(id, fn)
fn[HandlerID] = id
return id
}
function disposeHandlerID(fn) {
handlerMaps.delete(fn[HandlerID])
fn[HandlerID] = null
}
import { defineEvent } from "./index.js"
import assert from "node:assert/strict"
const x = {}
defineEvent(x, "onChange")
const handlers = [
() => console.log(0),
() => console.log(1),
() => console.log(2),
]
assert.equal(x.onChange, null)
x.onChange += handlers[0]
assert.notEqual(x.onChange, null)
x.onChange() // output 0
x.onChange += handlers[1]
x.onChange += handlers[2]
x.onChange -= handlers[0]
x.onChange += handlers[1]
x.onChange() // output 1, 2
x.onChange -= handlers[1]
x.onChange -= handlers[2]
assert.equal(x.onChange, null)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment