メソッドを変数に代入・受け渡しするとthisが失われる
オブジェクトのメソッドを変数に代入したり、別の関数に渡して呼び出したりすると、呼び出し時にレシーバーの情報が失われ、thisがオブジェクトを指さなくなります。
なぜエラーが出ないのか
出力: undefined
(エラーなし)- JavaScriptは何も報告しません。文法として正しいためです
出力: undefined- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 const counter = { count: 5, getCount: function() { return this.count; } }; 2 const getCount = counter.getCount.bind( ); ^ 3 console.log(getCount());
bind()に何も渡さないと、切り離されたgetCountを呼んでもthisはcounterを指しません。bind(counter)でthisを固定する必要があります。
直し方: (空) を counter にします。
パターン2
1 const logger = { prefix: "[LOG]", write(msg) { return this.prefix + " " + msg; } }; 2 function run(fn) { return fn("hi"); } 3 console.log(run(logger.write )); ^
logger.writeを関数として渡すと、run内でfn("hi")と呼ばれた時点でレシーバーの情報が失われます。bindでthisをloggerに固定してから渡す必要があります。
直し方: logger.write を logger.write.bind(logger) にします。
パターン3
1 const logger = { prefix: "[LOG]", write(msg) { return this.prefix + " " + msg; } }; 2 const { write } = logger; 3 console.log(write("hi") ); ^
分割代入で取り出したwriteはloggerとの結びつきを失っています。call(logger, ...)で明示的にthisを指定する必要があります。
直し方: write("hi") を write.call(logger, "hi") にします。
パターン4
1 const timer = { seconds: 0, tick: function() { this.seconds++; return this.seconds; } }; 2 const tick = timer.tick.bind( ); ^ 3 console.log(tick());
bind()に何も渡さないと、切り離されたtickを呼んでもthisはtimerを指しません。
直し方: (空) を timer にします。
パターン5
1 const wallet = { balance: 100, show() { return "$" + this.balance; } }; 2 function callLater(fn) { return fn(); } 3 console.log(callLater(wallet.show )); ^
wallet.showを関数として渡すと、callLater内で呼ばれた時点でレシーバーの情報が失われます。
直し方: wallet.show を wallet.show.bind(wallet) にします。
よくある誤解
「メソッドはどこから呼んでも同じオブジェクトに属する」という思い込みは誤りです。JavaScriptのthisは定義位置ではなく呼び出し方によって決まるため、切り離して渡した瞬間に本来のレシーバーとの結びつきは失われます。
実務での勘所
thisが失われるのは、JavaScriptのthisが関数を定義した場所ではなく、呼び出した瞬間の「ドットの左側に何があったか」で決まる仕組みだからです。obj.method()ならthisはobjになりますが、const f = obj.method; f()のように呼ぶと、呼び出し時にドットの左側に何も無いため、thisはundefined(厳格モード)かグローバルオブジェクトになります。定石の対処は3つあり、アロー関数(定義時の外側のthisをそのまま使う)、.bind(obj)でthisを固定したコピーを作る、あるいは呼び出し側でアロー関数に包んで渡す、のいずれかです。