Имея правильное значение 'this' в JS - PullRequest
2 голосов
/ 31 августа 2010

У меня есть два "объекта" Javascript, похожих на так ....

var Object2 = new (function() {
    this.FetchData = function(callback) {
        // do some stuff
        callback(data);
    };
});

var Object1 = new (function() {

    this.DisplayStuff = function() {

    };

    this.LoadData = function() {
        Object2.FetchData(this.OnData);
    };

    this.OnData = function(data) {
        // this == window
        this.DisplayStuff();   // doesn't work
    };

});

Когда Object1 получает обратный вызов для OnData, значение «this» устанавливается в window. Можно ли как-нибудь обойти это так, чтобы значением "this" внутри OnData был экземпляр Object1 вместо окна?

Ответы [ 5 ]

5 голосов
/ 31 августа 2010

Метод, обычно используемый в инфраструктурах, состоит в том, чтобы позволить вызывающей стороне решить, каким должен быть контекст this для функции обратного вызова.Таким образом, функция FetchData выглядит так (спасибо @Andy за сообщение о области по умолчанию, когда контекст имеет значение null или не определено - это глобальный объект),

this.FetchData = function(callback, context) {
    callback.call(context, data);
};

При вызове FetchData, passObject1 в качестве контекста для обратного вызова,

Object2.FetchData(this.OnData, this);

Другой вариант - связать функцию OnData с Object1, используя Function.prototype.bind

Object2.FetchData(this.onData.bind(this));
3 голосов
/ 31 августа 2010

Простой способ сделать это - сохранить ссылку на this в переменной, затем использовать метод call () :

this.LoadData = function() {
    var self = this;
    Object2.FetchData(function () { self.OnData.call(self) });
};

Если вы делаете это много, вы можете рассмотреть возможность использования метода .bind () для прототипа функции в ECMAScript 5-й редакции. Этот метод может быть реализован там, где не поддерживается:

// From Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
  Function.prototype.bind = function(){ 
    var fn = this, args = Array.prototype.slice.call(arguments),
        object = args.shift(); 
    return function(){ 
      return fn.apply(object, 
        args.concat(Array.prototype.slice.call(arguments))); 
    }; 
  };
}

И результирующий вызов функции:

this.LoadData = function() {
    Object2.FetchData(this.OnData.bind(this));
};

PrototypeJS - bind ()

0 голосов
/ 31 августа 2010

Неоднозначность «этого» действительно раздражает меня с помощью JavaScript. Так что я просто избегаю "этого" в максимально возможной степени. Я делаю это так:

var Object2 = (function() { // no need for a "new" keyword any more

    var self = {}; // instead of the auto-supplied "this", create my own "self"

    self.FetchData = function(callback) {
        // do some stuff
        callback(data);
    };
    return self; // don't forget to return "self"
});

var Object1 = (function() {

    var self = {}; // instead of the auto-supplied "this", create my own "self"

    self.DisplayStuff = function() {

    };

    self.LoadData = function() {
        Object2.FetchData(this.OnData);
    };

    self.OnData = function(data) {
        self.DisplayStuff();
    };

    return self; // don't forget to return "self"

});

Вы теряете возможность использовать Object.prototype с этой техникой, вы теряете возможность тестировать, используя instanceof, но вам никогда не нужно думать о том, что это такое.

0 голосов
/ 31 августа 2010

Для обратного вызова вы застряли с «this», являющимся окном (обычно). Однако вы можете добавить в Object1 элемент, который указывает на себя (часто он вызывает 'self', т.е. var self = this; "), а затем использовать его, то есть self.DisplayStuff ().

0 голосов
/ 31 августа 2010

Основным решением является принудительный контекст для обратного вызова с помощью apply.(Или call. Разница в том, как передаются параметры. Массив или «обычно»). См. Документацию MDC .

...