◆ 一度 bind したら再 bind できない
◆ できる版 bind + rebind 作ってみた

普段はあまり気にしないのですが ライブラリ作ってるときだと bind って上書きできたっけ?と毎回のように確認してしまいます
できるのは 1 回限りで bind 済み関数を bind しても効果はありません

普通に

function f(msg){
console.log(this, msg)
}

console.log(f.bind({a:1})("bind"))
console.log(f.call({a:1}, "call"))
console.log(f.apply({a:1}, ["apply"]))
{a: 1} "bind"
{a: 1} "call"
{a: 1} "apply"

this が {a:1} のオブジェクトになっています

bind 済みに

const b = f.bind({a:2})

console.log(b.bind({a:3})("bind * 2"))
console.log(b.call({a:3}, "bind + call"))
console.log(b.apply({a:3}, ["bind + apply"]))
{a: 2} "bind * 2"
{a: 2} "bind + call"
{a: 2} "bind + apply"

{a:2} のオブジェクトを bind 済みの関数を bind したり call したりしても this は {a:2} から変わりません
ただ new する場合は

console.log(new b("bind + new"))
f {}

新しいコンテキストで実行されます
prototype をうまく使えばできそうと思ったのですが

b.prototype = {a:4}
new b().a
// undefined

f.prototype = {a:5}
new b().a
// 5

となって bind 済みの関数の prototype を変更しても意味がないです
元の f 関数を変えれば反映されるのですが 元の f 関数にアクセスできるならそっちを call すればいいので意味がありません
bind 済みの関数しか参照できない場所でコンテキストを設定するということは無理そうです

アロー関数

ちなみにアロー関数は this が新しく作られないもので 言い換えれば関数が作られたコンテキストの this を関数作成時に bind してるみたいな動きです

const a = msg => console.log(this, msg)

console.log(a.bind({a:1})("arrow + bind"))
console.log(a.call({a:1}, "arrow + call"))
console.log(a.apply({a:1}, ["arrow + apply"]))
Window {} "arrow * 2"
Window {} "arrow + call"
Window {} "arrow + apply"

rebind

bind でコンテキスト設定したいけど後から bind したら上書きできるようにしたいと思ってこういうの作ってみました

Function.prototype.bind = function(ctx, ...args) {
const fn = (...a) => {
fn.function.apply(fn.context, [...fn.args, ...a])
}
if (this.context) {
fn.context = this.context
fn.args = [...this.args, ...args]
fn.function = this.function
} else {
fn.context = ctx
fn.args = args
fn.function = this
}
return fn
}

Function.prototype.rebind = function(ctx, ...args) {
const fn = (...a) => {
fn.function.apply(fn.context, [...fn.args, ...a])
}
if (this.context) {
fn.context = ctx
fn.args = [...this.args, ...args]
fn.function = this.function
} else {
fn.context = ctx
fn.ars = args
fn.function = this
}
return fn
}

bind は上書き不可能ないつもの bind ですが rebind 機能のためにちょっと中身が違ってます
rebind はすでに bind されていてもそれを無効にして bind するオブジェクトを設定できます

const c = f.bind({a:2})

console.log(c.rebind({a:3})("bind + rebind"))
console.log(b.call({a:3}, "bind + call"))
console.log(b.apply({a:3}, ["bind + apply"]))
{a: 3} "bind  + rebind"
{a: 2} "bind + call"
{a: 2} "bind + apply"

rebind したときだけ this が変わっています
call, apply は独自のものにしていないので rebind されず bind 済みなら this は変わりません

本来の bind と同じく引数の bind 機能も使えます

function d(...args){
console.log(this, ...args)
}

d.bind({a:1}, 1, 2).rebind({a:2}, 3, 4).bind({a:3}, 5)(6)
// {a: 2} 1 2 3 4 5 6