Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > JavaScript: для новичков > JS + OOP + AJAX


Автор: drainme 12.8.2010, 15:54
Всем привет , столкнулся с проблемой ооп в js. Есть псевдо класс, описывающий подключение в фоновом режиме - ajax запрос проще говоря. Проблема судя по всему в прототипах, ибо без классов все работает, но мне хочется разобраться и собрать библиотеку для ajax. Проблема в функции handle_response (33-45), там не видно свойства request. Еще есть сомнение по поводу onreadystatechange(25) - возмонжно я неправильно задал callback.  smile 
Код

function http_request(){
    this.request = null;
    this.callback= null; //function that runs after handling response
    this.stack = new Array();
    
    try {
        this.request = new XMLHttpRequest();
    } catch (e) {
        try {
            this.request = new ActiveXObject();
        }catch(e){}
    }
    if (this.request == null) throw('couldnot create request object');
}

http_request.prototype.send = function(method,url,data_array,callback) {
    if(method  && url){
        this.push_into_stack(method, url, data_array, callback);
    }
    
    if(this.request.readyState == 0 || this.request.readyState == 4){
        var settings = this.pop_out_of_stack();
        
        this.request.open(settings['method'], settings['url'], true);
        this.request.onreadystatechange = this.handle_response;
        this.callback = settings['callback'];
        this.request.send(this.prepare_data(settings['data_array']));
    }else if(this.stack.length > 0){
        setTimeout('this.send()', 3000);
    }
};

http_request.prototype.handle_response = function() {
    if(this.request.readyState == 4){
        if(this.request.status == 200){
            this.execute_response();
        }else{
            throw ('There was a mistake during ajax transfer.');
        }
    }else{ // delete
        alert(this.request.status.toString());
        alert(this.request.statusText);
        
    }
};

http_request.prototype.execute_response = function() {
    var response = this.request.responseText; // redo
    if(this.callback != null){
        this.callback(response);
    }
    if (this.stack.length > 0) {
        this.send();
    }
};

/* --- helpers --- */
http_request.prototype.prepare_data = function(data) {
    var str = new String;
    for ( var key in data) {
        str +=  escape(key)+'='+escape(data[key])+'&';
    }
    return str.substring(0, str.length - 1);
};

http_request.prototype.push_into_stack = function(method,url,data_array,callback){
    var tmpArray = new Array();
    tmpArray['method'] = method;
    tmpArray['url'] = url;
    tmpArray['data_array'] = data_array;
    tmpArray['callback'] = callback;
    this.stack.push(tmpArray);
};

http_request.prototype.pop_out_of_stack = function() {
    return this.stack.pop();
};

function text_callback(text){
    alert(text);
}

try{
    var request = new http_request();
    request.send("GET","http://virtual-dummy1/1.txt");
}catch(e){
    alert(e.toString());
}



Автор: Amphiluke 12.8.2010, 19:06
Цитата(drainme @  12.8.2010,  19:54 Найти цитируемый пост)
this.request.onreadystatechange = this.handle_response;

Попробуйте вместо этого так:
Код

var thisAlias = this;
this.request.onreadystatechange = function() {thisAlias.handle_response();}

Дело в том, что в обработчиках событий ключевое слово this указывает на объект, в котором происходит событие, в данном случае на объект XMLHttpRequest, а не на ваш собственный объект http_request.

Автор: IDVsbruck 12.8.2010, 20:53
Плохой пример. Переменная к моменту срабатывания события может быть недоступна. Надо использовать передачу параметров, а еще лучше замыкание.

Автор: Amphiluke 12.8.2010, 21:16
А чем это не замыкание? Цепочка областей видимости анонимной функции, назначаемой свойству onreadystatechange, включает объект вызова внешней функции. Сборщик мусора не удалит переменную thisAlias, пока вложенная функция будет ссылаться на объект вызова. Нет?

Автор: drainme 13.8.2010, 08:08
Большое спасибо за разъяснения, сам бы еще долго искал решение, но вот что интересно с моим прежним кодом" = this.handle_response", функция срабатывает(просто алерт вставил для теста), по поводу xmlhttpobject'a логично и тему можно закрывать, но почему this работает все-таки интересно

Автор: IDVsbruck 13.8.2010, 14:20
Цитата(Amphiluke @  12.8.2010,  21:16 Найти цитируемый пост)
А чем это не замыкание? Цепочка областей видимости анонимной функции, назначаемой свойству onreadystatechange, включает объект вызова внешней функции. Сборщик мусора не удалит переменную thisAlias, пока вложенная функция будет ссылаться на объект вызова. Нет?

Извини, дорогой ... Для раздела "JavaScript для новичков" пример вполне дееспособный, но если мы говорим о более серьезном подходе, то вариант никудышний.
Я молчу о том, что переменная thisAlias теоретически может быть переопределена на момент срабатывания хандлера - это, конечно же, маловероятно, но надежность кода страдает.
А вот то, что анонимная функция, назначенная onreadystatechange, НИЧЕГО пока не знает о своей "начинке" - это факт достаточно известный smile, поэтому GC может и подчистить приватную переменную. В данном случае спасает (может спасать) только то, что внутренний GC в JavaScript срабатывает по таймеру, а ответ с сервера в примере приходит быстрее. Но согласись, это же не решение задачи - так работать на грани фола. Именно для этого и придумали замыкания - чтобы отдать значение доступной переменной внутреннему механизму, и дальше ее не трогать и позволить удалиться. Советую уделять более пристальное внимание надежности и универсальности кода.

Добавлено через 11 минут и 9 секунд
Цитата(drainme @  13.8.2010,  08:08 Найти цитируемый пост)
Большое спасибо за разъяснения, сам бы еще долго искал решение, но вот что интересно с моим прежним кодом" = this.handle_response", функция срабатывает(просто алерт вставил для теста), по поводу xmlhttpobject'a логично и тему можно закрывать, но почему this работает все-таки интересно

Там где назначен this.handle_response, он существует, так как this в данном случае ссылается на объект типа http_request, то есть противоречий нет. А когда позже идет ссылка на this.request, то у handle_response такого свойства/функции нет. Можно получить через parent.request или обратиться просто через request - не стал пристально смотреть код и тестировать его, но думаю, это свойство (без this) будет доступно.

Автор: Amphiluke 13.8.2010, 15:57
IDVsbruck, спасибо, а вы не могли бы набросать примерчик?  smile  Даже отвлечемся от кода топикстартера. Просто какой-нибудь простой пример, в котором метод пользовательского объекта служил бы обработчиком события и при этом имел доступ ко всем свойствам объекта а также к экземпляру объекта Event, передаваемого в обработчик.

Вот я начну потихоньку.
Код

function CustomObj() { // конструктор
    this.prop = "this is object property";
}
CustomObj.prototype.methodHandler = function(e) {
    alert("event target: " + e.target);
    alert("object property: " /* + здесь нужно считать значение св-ва prop */);
}

/* ... */

var obj = new CustomObj();
document.addEventListener("click", obj.methodHandler, false); // или как-нибудь по-другому...



Можно сделать и так (типа еще «замыкание»  smile ):
Код

function CustomObj() { // конструктор
    this.prop = "this is object property";
}
CustomObj.prototype.methodHandler = function(e, __instance) {
    alert("event target: " + e.target);
    alert("object property: " + __instance.prop);
}
CustomObj.prototype.returnClickHandler = function() {
    var __instance = this;
    var __method = this.methodHandler;
    return function(e) { __method(e, __instance); };
}

var obj = new CustomObj();
document.addEventListener("click", obj.returnClickHandler(), false);

но это же опять неправильно, по-вашему?
Напишите, please, как вам видится наиболее грамотное решение.

Добавлено через 3 минуты и 11 секунд
Именно для этой простой задачки.

Автор: ksnk 13.8.2010, 18:20
drainme
там еще есть строка 29
Код

   setTimeout('this.send()', 3000);

оно работало когда-нибудь? Вот сюда тоже надо бы замыкание поставить.

Цитата(drainme @  13.8.2010,  08:08 Найти цитируемый пост)
 что интересно с моим прежним кодом" = this.handle_response", функция срабатывает(просто алерт вставил для теста)

проблема в том, что в этом случае при выполнении функции this будет указывать в объект window, а не в http_request

Автор: IDVsbruck 13.8.2010, 21:44
Цитата(Amphiluke @  13.8.2010,  15:57 Найти цитируемый пост)
Напишите, please, как вам видится наиболее грамотное решение.

Добавлено через 3 минуты и 11 секунд
Именно для этой простой задачки.

Человек дурню написал, а мы с тобой должны на поводу вестись? Хотя бы: 
Цитата(drainme @  12.8.2010,  15:54 Найти цитируемый пост)
Есть псевдо класс, описывающий подключение в фоновом режиме


Не считаю, что я должен учавствовать в споре "а слабо?" с заведома абсурдными данными.
В js (и фреймворках) мы же не переопределяем функции для обработчика, а создаем их и назначаем. Почему тут должно быть иначе? - Зачем делать обработчик в составе прототипа - дурня же! Если функция обработчика выполняет какие-то узкоспециализированные действия внутри класса, то делается переменная класса, указывающая на внутреннюю функцию, или еще лучше - грамотно написанная анонимная функция (действие-то внутреннее, можно постараться, чтобы казусов с загрязнение памяти не было).
Поэтому я принципиально не хочу поддерживать данный спор (дабы не было мысли, что я не могу или не знаю как и что делать - слава аллаху, за несколько лет работы школу получил неплохую).

Автор: Amphiluke 13.8.2010, 22:10
Ладно, ясно… что ничего не ясно  smile . Впрочем, причем здесь «а слабо?»? Я просто хотел увидеть кусок грамотного кода для дальнейших размышлений… о философии smile Разве кто-то спорит? Просто идет обмен информацией с целью поиска оптимальных путей.

Автор: IDVsbruck 14.8.2010, 00:14
Ага, а ну-ка, для начала покажи мне пример создания в javascript'е псевдо-класса smile

Автор: Amphiluke 14.8.2010, 08:35
Ну, вообще-то определение «псевдокласса» в Javascript как раз и осуществляется путем написания функции-конструктора, которая создает и инициализирует нужные атрибуты экземпляра, а также добавлением свойств и методов к объекту-прототипу (см. предыдущие сообщения). Ясно, что Javascript пока не обеспечивает поддержку классов, как этого, может быть, хотелось бы. Отсюда, наверное, присказка «псевдо-». Но такая терминология, вообще говоря, используется с указанными оговорками. Так что не стоит придираться к словам. Не об этом же речь. smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)