SetTimeout geri aramasına doğru "bu" bağlamı iletilsin mi?


251

Bağlamı nasıl aktarabilirim setTimeout? Ben aramak istiyorum this.tip.destroy()eğer this.options.destroyOnHidesonra 1000 ms. Bunu nasıl yapabilirim?

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

Yukarıdakileri denediğimde thispencereye atıfta bulunur.


4
Yinelenen bayrak gerçekten geçerli mi? Bu soru aslında daha önce sorulmuştu.
Sui Dream

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

Yanıtlar:


353

DÜZENLEME: Özet olarak, bu soruyu sormanın en yaygın yolu 2010 yılında geri çağrıldığında, setTimeoutişlev çağrısının yapıldığı bağlama bir başvuru kaydetmekti , çünkü setTimeoutişlevi thisglobal nesneye işaret ederek yürütür :

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

O zamandan bir yıl önce piyasaya çıkan ES5 spesifikasyonunda, bindyöntemi tanıttı , bu orijinal cevapta önerilmedi çünkü henüz geniş çapta desteklenmedi ve kullanmak için çoklu dolgulara ihtiyacınız vardı, ancak şimdi her yerde:

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

bindFonksiyon ile yeni bir işlevi oluşturur thisdeğeri önceden doldurulmuş.

Şimdi modern JS'de, bu ok işlevlerinin ES6'da çözdüğü sorun :

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

Ok işlevlerinin thiskendine ait bir değeri yoktur, eriştiğinizde, thisilgili sözcük kapsamının değerine erişirsiniz .

HTML5 ayrıca zamanlayıcıları 2011'de standart hale getirdi ve artık argümanları geri arama işlevine iletebilirsiniz:

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

Ayrıca bakınız:


3
İşe yarıyor. Kavramı bir jsbin betiği ile test ettim: jsbin.com/etise/7/edit
John K

1
Bu kod gereksiz bir değişken oluşturmayı içerir (fonksiyon geneli kapsamı vardır); thisişleve doğru bir şekilde geçtiyseniz, bu durumda, map (), forEach () vb. için, daha az kod, daha az CPU çevrimi ve daha az bellek kullanarak bu sorunu çözmüş olursunuz. *** Bakınız: Misha Reyzlin'in cevabı.
HoldOffHunger

222

@CMS'nin yanıtladığı işlev sarıcısına hazır kısayollar (sözdizimsel şeker) vardır. (İstediğiniz bağlamın olduğunu varsayarak this.tip.)


ECMAScript 5 ( mevcut tarayıcılar , Node.js) ve Prototype.js

ECMA-262, 5. sürüm (ECMAScript 5) veya Node.js ile uyumlu tarayıcıyı hedeflerseniz kullanabilirsiniz Function.prototype.bind. Kısmi işlevler oluşturmak için isteğe bağlı olarak işlev bağımsız değişkenlerini iletebilirsiniz .

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

Yine, sizin durumunuzda şunu deneyin:

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

Aynı işlevsellik Prototype'de (başka kütüphaneler?) De uygulanmıştır.

Function.prototype.bindözel geriye dönük uyumluluk istiyorsanız bu şekilde uygulanabilir (ancak lütfen notlara dikkat edin).


ECMAScript 2015 ( bazı tarayıcılar , Node.js 5.0.0+)

Son teknoloji geliştirme (2015) için, ECMAScript 2015 (Harmony / ES6 / ES2015) belirtiminin ( örnekler ) bir parçası olan yağ ok işlevlerini kullanabilirsiniz .

Bir ok işlevi ifadesi ( yağ oku işlevi olarak da bilinir ) işlev ifadelerine kıyasla daha kısa bir sözdizimine sahiptir ve thisdeğeri [...] sözcüksel olarak bağlar .

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

Sizin durumunuzda şunu deneyin:

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

jQuery

Zaten jQuery 1.4+ kullanıyorsanız this, bir işlevin bağlamını açıkça ayarlamak için hazır bir işlev vardır.

jQuery.proxy () : Bir işlevi alır ve her zaman belirli bir bağlama sahip olacak yeni bir işlev döndürür.

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

Sizin durumunuzda şunu deneyin:

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

Underscore.js , lodash

Underscore.js'de ve ayrıca lodash'da _.bind(...)1 , 2 olarak kullanılabilir

bind Bir işlevi bir nesneye bağlama, yani işlev her çağrıldığında, değerininthisnesne olacağı anlamına gelir. İsteğe bağlı olarak, kısmi uygulama olarak da bilinen önceden doldurmak için işleve argümanları bağlayın.

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

Sizin durumunuzda şunu deneyin:

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


Neden varsayılan değil func.bind(context...)? Bir şey mi özledim?
aTei

Bunu her aradığınızda sürekli olarak yeni bir işlev yaratmaya devam ediyor mu? Her tuşa basıldıktan sonra sıfırlanan bir arama zaman aşımı var ve ben sadece bu 'bağlı' yöntemi yeniden kullanım için bir yerde önbelleğe alınması gibi görünüyor.
Triynko

@Triynko: Bir işlevi pahalı bir işlem olarak bağlamayı görmezdim, ancak aynı bağlı işlevi birden çok kez çağırırsanız, bir referans da tutabilirsiniz: var boundFn = fn.bind(this); boundFn(); boundFn();örneğin.
Joel Purra

30

Internet Explorer dışındaki tarayıcılarda, gecikmeden sonra parametreleri işleve birlikte iletebilirsiniz:

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

Yani, bunu yapabilirsiniz:

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

Bu, performans açısından bir kapsam aramasından ( thiszaman aşımı / aralık ifadesinin dışındaki bir değişkene önbellekleme ) ve ardından bir kapatma ( $.proxyveya kullanarak Function.prototype.bind) oluşturmadan daha iyidir .

Webreflection'dan IE'lerde çalışmasını sağlayan kod :

/*@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);
    }
});
@*/

1
Prototip zincirini kullanarak bir sınıf oluştururken ve metotlarınız prototip metotlarıdır ... 'bind' yöntem içinde 'bu' olanı değiştirecek tek şeydir. Geri aramaya bir parametre ileterek, işlevde 'bu' öğesinin ne olduğunu değiştirmezsiniz, bu nedenle bu tür bir prototip işlevi, içinde başka bir prototip yönteminin yapabileceği gibi 'this' kullanılarak yazılamaz. Bu tutarsızlığa yol açar. Bağlama aslında istediğimiz şeye en yakın şeydir ve kapak daha yüksek arama performansı için bir kereden fazla oluşturmak zorunda kalmadan 'bu' içinde önbelleğe alınabilir.
Triynko

4

NOT: Bu IE'de çalışmaz

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);

2

Eğer kullanıyorsanız underscore, kullanabilirsiniz bind.

Örneğin

if (this.options.destroyOnHide) {
     setTimeout(_.bind(this.tip.destroy, this), 1000);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.