web-dev-qa-db-ja.com

正しい「this」コンテキストをsetTimeoutコールバックに渡しますか?

コンテキストをsetTimeoutに渡すにはどうすればよいですか? 1000ミリ秒後にthis.options.destroyOnHideの場合はthis.tip.destroy()を呼び出したいです。どうやってやるの?

if (this.options.destroyOnHide) {
     setTimeout(function() { this.tip.destroy() }, 1000);
} 

上記を試してみると、thisはウィンドウを指します。

212

EDIT:要約すると、2010年にこの質問が行われたとき、この問題を解決する最も一般的な方法は、setTimeoutのコンテキストへの参照を保存することでしたsetTimeoutはグローバルオブジェクトを指すthisで関数を実行するため、関数呼び出しが行われます。

var that = this;
if (this.options.destroyOnHide) {
     setTimeout(function(){ that.tip.destroy() }, 1000);
} 

その1年前にリリースされたES5仕様では、 bind method が導入されましたが、これはまだ広くサポートされておらず、使用するためにポリフィルが必要だったため、元の回答では提案されませんでしたしかし、今ではどこにでもあります:

if (this.options.destroyOnHide) {
     setTimeout(function(){ this.tip.destroy() }.bind(this), 1000);
}

bind関数は、this値が事前に入力された新しい関数を作成します。

現在、現代のJSでは、これはまさに矢印関数が ES6 で解決する問題です。

if (this.options.destroyOnHide) {
     setTimeout(() => { this.tip.destroy() }, 1000);
}

矢印関数にはthisの値はありません。それにアクセスすると、囲んでいるレキシカルスコープのthisの値にアクセスしています。

HTML5も 標準化されたタイマー 2011年に戻りました。コールバック関数に引数を渡すことができます。

if (this.options.destroyOnHide) {
     setTimeout(function(that){ that.tip.destroy() }, 1000, this);
}

こちらもご覧ください:

283
CMS

@CMSが応答する関数ラッパーへの既製のショートカット(構文糖)があります。 (以下では、必要なコンテキストがthis.tipであると想定しています。)


ECMAScript 5現在のブラウザー 、Node.js)およびPrototype.js

ECMA-262と互換性のあるブラウザー、第5版(ECMAScript 5) または Node.js をターゲットとする場合、 Function.prototype.bind を使用できます。オプションで、任意の関数引数を渡して 部分関数 を作成できます。

fun.bind(thisArg[, arg1[, arg2[, ...]]])

繰り返しますが、あなたの場合、これを試してください:

if (this.options.destroyOnHide) {
    setTimeout(this.tip.destroy.bind(this.tip), 1000);
}

同じ機能が Prototypeで実装 (他のライブラリ?)でもあります。

Function.prototype.bindは次のように実装できます カスタムの後方互換性が必要な場合(ただし注意事項を確認してください).


ECMAScript 2015一部のブラウザー 、Node.js 5.0.0+)

最先端の開発(2015)では、太い矢印関数を使用できます。これは ECMAScript 2015(Harmony/ES6/ES2015)仕様の一部 )。

arrow function expression (別名fat arrow function)は、関数式と比較して構文が短く、this値[...]を字句的にバインドします。

(param1, param2, ...rest) => { statements }

あなたの場合、これを試してください:

if (this.options.destroyOnHide) {
    setTimeout(() => { this.tip.destroy(); }, 1000);
}

jQuery

すでにjQuery 1.4+を使用している場合、関数のthisコンテキストを明示的に設定する既製の関数があります。

jQuery.proxy() :関数を受け取り、常に特定のコンテキストを持つ新しい関数を返します。

$.proxy(function, context[, additionalArguments])

あなたの場合、これを試してください:

if (this.options.destroyOnHide) {
    setTimeout($.proxy(this.tip.destroy, this.tip), 1000);
}

nderscore.jslodash

Underscore.jsとlodashで_.bind(...)として利用可能です12

bind 関数をオブジェクトにバインドします。つまり、関数が呼び出されるたびに、thisの値がオブジェクトになります。必要に応じて、関数に引数をバインドして事前入力します。これは部分適用とも呼ばれます。

_.bind(function, object, [*arguments])

あなたの場合、これを試してください:

if (this.options.destroyOnHide) {
    setTimeout(_.bind(this.tip.destroy, this.tip), 1000);
}

バインドjquerynderscore.jsecmascript-5prototypejsnode。 js

220
Joel Purra

Internet Explorer以外のブラウザーでは、遅延後に関数にパラメーターを一緒に渡すことができます。

var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);

だから、あなたはこれを行うことができます:

var timeoutID = window.setTimeout(function (self) {
  console.log(self); 
}, 500, this);

これは、スコープルックアップ(thisをタイムアウト/間隔式以外の変数にキャッシュする)よりもパフォーマンスの点で優れており、クロージャーを作成する($.proxyまたはFunction.prototype.bindを使用して)。

IEで Webreflection から動作させるためのコード:

/*@cc_on
(function (modifierFn) {
  // you have to invoke it as `window`'s property so, `window.setTimeout`
  window.setTimeout = modifierFn(window.setTimeout);
  window.setInterval = modifierFn(window.setInterval);
})(function (originalTimerFn) {
    return function (callback, timeout){
      var args = [].slice.call(arguments, 2);
      return originalTimerFn(function () { 
        callback.apply(this, args) 
      }, timeout);
    }
});
@*/
30
Misha Reyzlin

注:これはIEでは機能しません

var ob = {
    p: "ob.p"
}

var p = "window.p";

setTimeout(function(){
    console.log(this.p); // will print "window.p"
},1000); 

setTimeout(function(){
    console.log(this.p); // will print "ob.p"
}.bind(ob),1000);
3
gumkins

underscore を使用している場合は、bindを使用できます。

例えば。

if (this.options.destroyOnHide) {
     setTimeout(_.bind(this.tip.destroy, this), 1000);
}
2
Sam