2010-06-09 2 views
1

У меня возникли проблемы с работой функции обратного вызова. Вот мой код:Проблема определения с помощью обратного вызова Javascript

SomeObject.prototype.refreshData = function() 
{ 
    var read_obj = new SomeAjaxCall("read_some_data", { }, this.readSuccess, this.readFail); 
} 

SomeObject.prototype.readSuccess = function(response) 
{ 
    this.data = response; 
    this.someList = []; 
    for (var i = 0; i < this.data.length; i++) 
    { 
     var systemData = this.data[i]; 
     var system = new SomeSystem(systemData); 
     this.someList.push(system); 
    } 
    this.refreshList(); 
} 

В основном SomeAjaxCall делает запрос ajax для данных. Если он работает, мы используем callback 'this.readSuccess', и если он терпит неудачу 'this.readFail'.

Я понял, что это «this» в SomeObject.readSuccess является глобальным (как объект окна), потому что мои обратные вызовы вызывается как функции, а не методы-члены. Я понимаю, что мне нужно использовать закрытие, чтобы сохранить «это», однако я не смог заставить это работать.

Если кто-то может показать мне, что я должен делать, я был бы очень признателен. Я все еще обдумываю, как работают замыкания, и конкретно, как они будут работать в этой ситуации.

Спасибо!

ответ

2

Ну самый простой, что нужно сделать, это просто обернуть «this.readSuccess» в другой функции:

SomeObject.prototype.refreshData = function() 
{ 
    var obj = this; 
    var read_obj = new SomeAjaxCall("read_some_data", { }, 
    function() { obj.readSuccess(); }, function() { obj.readFail(); }); 
} 

Некоторые фреймворков предоставляют утилиту для «привязки» функцию к объекту, который просто означает что он создает для вас одну из этих небольших функций. Обратите внимание, что переменная «obj» будет «запоминаться» этими небольшими функциями, поэтому, когда ваши обработчики называются «этой» ссылкой, это будет объект, который использовался для вызова «refreshData».

+0

Действительно , то, что происходит здесь, заключается в том, что 'obj' блокируется в замыкании, создаваемом при' function() {obj.readSuccess(); } '. Нет другого способа сделать это, если вы не используете такую ​​библиотеку, как Prototype или jQuery; они имеют функцию '.bind()', которая будет делать создание закрытия прозрачно. – MvanGeest

+0

Не для nit-pick, но для потомков StackOverflow версия jQuery называется «прокси», а не «bind». – Pointy

+0

Идеальный смысл. Прокси-функция jQuery работает, это здорово. Для полноты, однако, я все еще немного смущен, как бы я справился с этим, если бы не использовал jQuery. В моем примере readSuccess имеет 1 аргумент, respnse, который заполняется позднее. Поэтому мне не имеет смысла говорить function() {obj.readSuccess (wah ??); } так как вау ??? должен быть передан позже. Я попробовал функцию() {obj.readSuccess; } без везения. Есть предположения? – nazbot

0

Следующий сайт, похоже, говорит о том, что проблема может быть больше в вашем «классе», чем в использовании. Если вы читаете «переписать с использованием свойств прототипа», они пишут, что этот конкретный метод структурирования класса будет поддерживать методы глобальными, а не основанными на экземплярах. Возможно, еще один способ создания?

http://devedge-temp.mozilla.org/viewsource/2001/oop-javascript/

2

Ваша проблема здесь не точно замыкание или проблема обзорной. Проблема в том, что когда вы назначаете this.readSuccess переменной, вы сами присваиваете функцию без какого-либо понятия объекта, к которому она принадлежит.

Таким же образом, вы можете взять регулярный, «Stand-Alone» функцию и использовать его в качестве метода объекта:

function hello() { 
    alert("Hello "+this.planet); 
} 
var planet = "Earth"; 
hello(); // -> 'Hello Earth' 

var Venus = { 
    planet: "Venus" 
}; 
hello.apply(Venus); // -> 'Hello Venus' 
Venus.hello = hello; 
Venus.hello(); // -> 'Hello Venus' 

И ваша проблема может быть воспроизведен в этом примере

var helloVenus = Venus.hello; 
helloVenus(); // -> 'Hello Earth' 

Итак, ваша проблема заключается в том, чтобы назначить this.readSuccess для некоторой переменной и назвал ее как метод этого. Что можно сделать с закрытием, как продемонстрировал Уокей. Поскольку я не знаю, что на самом деле делает «SomeAjaxCall», трудно узнать, действительно ли значение this потеряно, и если действительно требуется var obj = this.Скорее всего, что это не так, так что вы можете быть в порядке с таким кодом:

var helloVenus = function() { Venus.hello() } 
helloVenus(); // -> 'Hello Venus' 

В вашем случае, это было бы (редактирование: добавив аргументы, передаваемые в обработчик):

SomeObject.prototype.refreshData = function() 
{ 
    var read_obj = new SomeAjaxCall(
    "read_some_data", 
    { }, 
    function() { this.readSuccess.apply(this, arguments) }, 
    function() { this.readFail.apply(this, arguments) } 
); 
} 

Как уже отмечалось ранее, несколько js-фреймворков предлагают функцию bind для упрощения такого рода проблем. Но вам не нужны полные рамки только для этого: вот прекрасно Function#bind метод, который работает в простой JavaScript:

Function.prototype.bind = function(obj) { 
    var __method = this; 
    var args = []; 
    for(var i=1; i<arguments.length; i++) 
     args.push(arguments[i]); 
    return function() { 
     var args2 = []; 
     for(var i=0; i<arguments.length; i++) 
      args2.push(arguments[i]); 
     return __method.apply(obj, args.concat(args2)); 
    }; 
} 

С помощью Function#bind, вы можете написать:

SomeObject.prototype.refreshData = function() 
{ 
    var read_obj = new SomeAjaxCall(
    "read_some_data", 
    { }, 
    this.readSuccess.bind(this), 
    this.readFail.bind(this) 
); 
} 
+0

Это отличный ответ. Большое спасибо за информацию. – nazbot

+0

и bind теперь является частью ECMAScript 5: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind – w00t

Смежные вопросы