LoginSignup
1
1

More than 5 years have passed since last update.

javascriptでオブジェクトを使ったらsetTimeout関数のthis問題に遭遇

Posted at

setTimeout関数の失敗例

<script src="jquery-3.3.1.min.js"></script><!-- jQueryをロード -->
<script>
$(function(){
    'use strict';
    class Test{
        constructor(){this.v="成功";}
        alert(){
            alert(this.v);
        }
    }

    const t=new Test(); //オブジェクトを作成

    $(test01).click(function(e){
        setTimeout(t.alert, 100);//失敗例
    });
});
</script>
<button id="test01">test01</button>

このプログラムを実行すると

undefined.png

undefinedが返ってきた。1

原因はthis問題

setTimeout() にメソッドを渡すとき、コードが実行される際の this の値が想定とは異なるかもしれません。

setTimeout() によって実行されるコードは、setTimeout が呼び出された関数とは別の実行コンテキスト内から呼び出されます。呼び出された関数に this キーワードを設定する通常の規則を適用して、呼び出しあるいは bind で this を設定しなければ、非 strict モードでは global (または window)、strict モードでは undefined になります。

注記: setTimeout コールバックの既定の this の値は、strict モードであっても undefined ではなく、window オブジェクトです。

出典: MDN Web Docs WindowOrWorkerGlobalScope.setTimeout() "this" 問題

試したら非 strict モードでも strict モード2でも setTimeout コールバックの既定の this の値は、 window オブジェクトだった。 setTimeout(callback) は内部で callback.call(window) を呼んでるようだ。

javascriptのオブジェクトの挙動が怪しい!

どのように書けばいいかというと、setTimeout(function(){t.alert()}, 100)と書けば成功。
t.alertなら関数扱いでt.alert()ならオブジェクトのメソッド扱いになるようだ。括弧があるかないかの違いで挙動が変わる。

fun=t.alert; fun();とやればt.alert内のthisの値がundefinedになった。
t.alertがオブジェクトのメソッドなので明示的に書かなくてもfun=t.alert; fun.call(t)を呼べばいいのに。:disappointed_relieved:

setTimeout(t.alert.bind(t), 100)と書いてもsetTimeout(function(){t.alert.call(t)}, 100)と書いてもsetTimeout($.proxy(t.alert,t), 100)と書いても成功。
t.alert.bind(t)$.proxy(t.alert,t)の内部を見てないがfunction(){t.alert.call(t)}を返す動きをしてるようだ。

怪しいとはいえ、javascriptのオブジェクトは便利。thisの値3を気にしながらオブジェクトを使えばいい。:blush:


  1. IEではclassもアロー関数も動かなかった。FirefoxかGoogle Chromeで動いた。 

  2. 'use strict'; を書けば strict モードになる。 

  3. この問題とは別だが、アロー関数にすればそれが定義されている場所でのthisになるようだ(参考)。 

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1