◆ prototype の __proto__ 変更できなかった
  ◆ Text.prototype やその先のプロトタイプのプロパティが見えてしまう

EventTarget の代わり

EventTarget の記事のコメントで Safari は未対応と教えていただきました
EventTarget を直接使えないなら EventTarget 継承してるなにかを使えばいいかと考えて思いついたのが Text です

Node や Element や HTMLElement は new できません
CustomElements に登録すれば HTMLElement を継承して new できますが そのためだけにカスタム要素を登録のも変ですし
Option や Image など new できるものも一部ありますが HTMLElement の中でなぜそれなのか感も残ります
そこでちょうど特別感あって new できる Text が良さそうかなと思いました

イベント関係が使えればいいので DOM の Node 系以外にも XMLHttpRequest とか FileReader とか WebSocket とか色々思いつきますが EventEmitter 代わりにしては気持ち的に重すぎます

__proto__ を変えると

普通に

class EE extends Text {}

とすると余計なプロパティやメソッドがいっぱいついてきます
困ることは特に無いですが 気持ち悪いので EE.prototype の __proto__ を EventTarget.prototype に設定します
こうすれば Text.prototype はプロトタイプチェーンにないので EventTarget のメソッドやプロパティのみになります

しかし これが Safari だとうまく動きませんでした
まず Text 以外の場合の挙動です

class A {
get a() { return 10 }
}
class B extends A {}

Object.setPrototypeOf(B.prototype, { foo: 20 })

const b = new B()
console.log(b.a, b.foo)
// undefined 20

A を継承していますが __proto__ を繋ぎ変えているので B のインスタンスは A ではなく {foo: 20} を見ます
その結果 b.a は undefined で b.foo は 20 になります
これは Chrome も Firefox も Safari も一緒でした

次に A を Text に置き換えます

class C extends Text {}

Object.setPrototypeOf(C.prototype, { foo: 20 })

const c = new C()
console.log(c.foo, c.nodeName)
// undefined #text

Text を継承した C の prototype の __proto__ を B と同様に { foo: 20 } に変更しています
それなのに c.foo は undefined です
さらに c.nodeName が存在して #text が入っています

Chrome や Firefox では c.foo に 20 が入っていて c.nodeName は undefined です
Safari の場合は Text を継承したクラスの prototype の先を変えられないようです

Safari でも直接 EventTarget につなぐ

Text のメソッドやプロパティがあるだけで困るわけでもないですが できないとなるとやりたくなるものなのでやってみました

class EE {
constructor() {
const elem = document.createElement("_")
Object.setPrototypeOf(elem, this.__proto__)
return elem
}

on(...a) { this.addEventListener(...a) }
off(...a) { this.removeEventListener(...a) }
emit(type, ...args) { this.dispatchEvent(new CustomEvent(type, { detail: args })) }
}
Object.setPrototypeOf(EE.prototype, EventTarget.prototype)

console.log(new EE().nodeName)
// undefined

console.log(new EE().addEventListener)
// function addEventListener() { [native code] }

ここまで来ると class 構文使う必要があるのかってくらいです
constructor でオブジェクトを作ってそれを return します
new できる必要性もなくなったので createElement に _ を指定して HTMLUnknownElement にしました
ただし その __proto__ は EE.prototype にしていてちゃんと EE クラスのメソッドが使えるようになっています
また EE.prototype.__proto__ は EventTarget.prototype なので Element や Node などのメソッドは含まれません

本来の EventTarget の機能も問題なく動いてます

const ee = new EE()
ee.on("aa", eve => {
console.log(eve.type, eve.detail)
})
ee.emit("aa", 12)
// "aa" [12]