定義した名前に依存したくないし 一旦定義してから呼び出す必要もある
作ってそのまま呼び出したいから 引数として自分自身を受け取りたい
arguments.callee という良さそうなものがあるのに strict mode (es modules) だと使えない
あと arguments がないから通常の JavaScript でもアロー関数で使えない

というわけで

const rec = f => f.bind(f, f)

実行

const rf = rec((self, x) => x > 0 ? [x, ...self(x - 1)] : [x])
rf(3)
// [3, undefined]

これだと [3, 2, 1, 0] じゃない
self を呼び出すときに引数で self を入れて

const rf = rec((self, x) => x > 0 ? [x, ...self(self, x - 1)] : [x])
rf(3)
// [3, 2, 1, 0]

再帰関数の関数を途中で変えるなんてしないし self 以外を入れることはなし
なら自動で self が bind されてるようにしたい

const rec = f => {
const ff = (...a) => f(ff, ...a)
return ff
}
const rf = rec((self, x) => x > 0 ? [x, ...self(x - 1)] : [x])
rf(3)
// [3, 2, 1, 0]

ff を一旦作ってるのが気になるけど仕方なし

bind 位置を自分で選べるようにしてみた版

const recm = fn => {
const w = fn((...a) => w(...a))
return w
}
const f = function(v) { return v > 0 ? [v, ...this(v - 1)] : [v] }
const rf = recm(r => f.bind(r))
rf(3)
// [3, 2, 1, 0]

recm に渡す関数では 引数として渡される関数が自分が返した関数になる
完全同じ参照である関数にはできないから 引数はラップ関数になってて呼び出したら自分が返した関数が実行される
引数はそのまま渡されるからラップ関数経由してることは気にする必要なし
ただ 引数で受け取る関数は recm が終わるまで実行したらダメ
関数を返すまでは初期化されてないので 「Cannot access 'w' before initialization」 って言われる