Javascript этот объект внутри интервалов / тайм-аутов

#javascript #this #setinterval

#javascript #это #setinterval

Вопрос:

У меня есть метод, который представляет собой большой оператор setInterval, и ему нужен доступ к this объекту объекта, которому принадлежит метод, изнутри интервала. Я реализовал простое закрытие, но оно не кажется очень элегантным:

 connect: function(to, rate, callback){
    var cthis = this,                             //set cthis to this,
    connectIntervalID = setInterval(function(){
        if(cthis.attemptConnect(to)){             //reference it here,
            clearInterval(connectIntervalID)
            cthis.startListening(10)              //here,
            callback amp;amp; callback.apply(cthis, []) //and here
        }
    }, rate)
}
  

Вы также могли бы сделать это с помощью apply или call, если вы хотите использовать this вместо cthis

 connect: function(to, rate, callback){
    var cthis = this,
    tempFunc = function(){
        if(this.attemptConnect(to)){                 
            clearInterval(connectIntervalID)
            this.startListening(10)              
            callback amp;amp; callback.apply(this, []) 
         }
     }�       
     connectIntervalID = setInterval(function(){tempFunc.apply(cthis, [])}, rate)
 }
  

Однако это кажется еще хуже…

Комментарии:

1. Это JavaScript, к лучшему или к худшему.

Ответ №1:

Использование a .bind делает это немного лучше (на мой взгляд, вы можете согласиться, а можете и не согласиться):

код поддержки:

 function $A(args){
   var out = [];
   for(var i=0, l=args.length; i<l; i  ){ out.push(args[i]); }
   return out;
}

Function.prototype.bind = function() {
   var __method = this, args = $A(arguments), object = args.shift();

   return function() {
      return __method.apply(object || this, args.concat( $A(arguments) ));
   };
};
  

и ваш код становится:

 connect: function(to, rate, callback){
    connectIntervalID = setInterval((function(){
        if(this.attemptConnect(to)){             //reference it here,
            clearInterval(connectIntervalID)
            this.startListening(10)              //here,
            callback amp;amp; callback.apply(this, []) //and here
        }
    }).bind(this), rate)
}
  

Но я боюсь, что у вас не получится намного лучше.

Комментарии:

1. MDC отмечает, что реальный метод привязки ES5 отличается по поведению — вы захотите протестировать как в ES5, так и в браузерах, отличных от ES5. developer.mozilla.org/en/JavaScript/Reference/Global_Objects /…

Ответ №2:

Ваш первый пример является более или менее стандартным способом сделать это. Моим единственным предложением было бы назвать вашу переменную как-то иначе, чем cthis; сделайте ее описательной для привязываемого объекта.

Javascript 1.8.5 добавляет Function.prototype.bind для решения этой проблемы другим способом, но это не является полезным решением для большинства людей.

Комментарии:

1. Почему bind это не полезное решение для большинства людей? Тривиально выполнить обнаружение функции и добавить ее, когда она не существует.

2. @theazureshadow это полезно, но только если вам это нужно неоднократно. Для одноразового использования код обнаружения функций и поддержки того не стоит.

3. @Box9: Я понимаю, к чему вы клоните, но я думаю, что многие люди сочли бы выигрыш в удобочитаемости оправданным при обычном использовании (особенно если они используют его в нескольких проектах всякий раз, когда возникает необходимость). Это полезный инструмент в наборе инструментов, потому что он описывает закрытие, а не оставляет его неявным (не говоря уже о потенциально случайном).

4. это 1.8.5, только в новейших firefoxes, которые я разрабатываю в Chrome 12, у которого есть 1.6 или 1.7, но в противном случае это сработало бы

Ответ №3:

Я бы выделил setInterval функцию в ее собственную функцию, прикрепленную к тому же объекту, что и connect . Таким образом, будет ясно, что this относится к одному и тому же объекту:

 connect: function (to, rate, callback) {
    var obj = this;
    var intervalId = setInterval(function () {
        obj.connectInterval(intervalId, callback);
    }, rate);
},
connectInterval: function (intervalId, callback) {
    if (this.attemptConnect(to)) {
        clearInterval(intervalId);
        this.startListening(10);
        callback amp;amp; callback.apply(this, []);
    }
}