У меня есть два 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 вместо окна?

2
T. Stone 31 Авг 2010 в 19:32

5 ответов

Лучший ответ

Простой способ сделать это - сохранить ссылку на 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));
};

Прототип JS - связывания ()

3
Andy E 31 Авг 2010 в 15:39

Неоднозначность «этого» действительно раздражает меня с помощью 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
morgancodes 31 Авг 2010 в 15:52

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

0
Anthony -GISCOE- 31 Авг 2010 в 15:39

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

0
Alex Mcp 31 Авг 2010 в 15:38

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

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

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

Object2.FetchData(this.OnData, this);

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

Object2.FetchData(this.onData.bind(this));
5
Anurag 31 Авг 2010 в 15:59