◆ デフォルトを作る仕組みとオプションで設定できるように

あるインスタンスが別のインスタンスをプロパティに持ってるなんてことは日常茶飯事です

class X {
method() {}
}
class Y {
x = new X()

method() {
this.x.method()
}
}
const y = new Y()
y.method()

この場合 y.x がそうです
y の method で x を使うので Y の処理は X に依存してます

こういう感じで constructor の中で new してプロパティにセットするのがスタンダードですが これだと扱いづらいところもあります
Y を使う側で X の代わりに別のにしたかったり 複数の y で同じ x を共有したり

インスタンス差し替えたいことがあるたびに楽でいい方法は無いのかと思ってます

後からプロパティ変える

理想的なクラスらしく作ってれば new の段階では 通信始めたりとかファイル操作したりとか外部を更新する処理はなくて インスタンスのプロパティを作るだけ……のはずです
実際に行いたい何らかの処理は インスタンス作成後に実行のメソッドを呼び出して始めて実行されます
インスタンス自体を作る処理のコストはあるものの ほとんどないようなものと考えられるので new したあとにプロパティを置き換えればそれでおっけいです

new Y()
y.x = new X2()

とは言っても実際には依存する X のインスタンスを使ったメソッド呼び出しから他のプロパティを作ったりして y.x だけを変えれば済む問題じゃないケースもあります

引数

となるとやっぱり初期値として引数に入れることになります

これの問題は毎回指定するのが面倒なのと 1 つの引数として依存インスタンスを受け渡しするのもなぁと思うところ
これらは省略時のデフォルトと順番に渡すタイプじゃなくてオブジェクトを使った名前渡しにすればあまり気にならなくなりました

共通処理を行うクラスが A で 依存インスタンスをもつクラスはこれを継承します
クラス B では foo と bar に Foo と Bar クラスを使います

class Foo {
method() {
console.log("Foo")
}
}
class Bar {
method() {
console.log("Bar")
}
}

class A {
static deps = {}

constructor(options = {}) {
Object.assign(this, options.deps)
for (const [name, get] of Object.entries(this.constructor.deps)) {
if (!(name in this)) {
this[name] = get()
}
}
}
}

class B extends A {
static deps = {
foo: () => new Foo(),
bar: (x => () => x)(new Bar()),
}
}

static の deps にオブジェクトでデフォルトのインスタンスを設定します
キーがプロパティ名でバリューがデフォルトインスタンスを返す関数です
毎回 new すればインスタンスごとに依存インスタンスも作成しますし 同じものを返す関数なら全体で共通です

コンストラクタの引数の deps プロパティを指定すると依存インスタンスを指定できます

const b1 = new B()
const b2 = new B()

b1
// B {foo: Foo, bar: Bar}

b2
// B {foo: Foo, bar: Bar}

b1.foo.method()
// Foo

b1.bar.method()
// Bar

b2.foo.method()
// Foo

b2.bar.method()
// Bar

b1.foo === b2.foo
// false

b1.bar === b2.bar
// true

const foo = new Foo()
const b3 = new B({ deps: { foo } })
b3.foo === foo
// true

b3.bar === b1.bar
// true

以前の require 置き換えたりよりは複雑にもならなくていいんじゃないかと思います