Свойства объекта Javascript становятся неопределенными после возврата ajax-запроса - PullRequest
5 голосов
/ 17 июня 2010

если у вас есть объект и установлено свойство для него, вы можете получить доступ к этому свойству в функции, вызываемой для этого объекта.но если вы вызываете функцию и делаете запрос ajax, так что из onreadystatechange вызывается другая функция, эта вторичная функция ответа не имеет доступа к свойству.Это немного сбивает с толку, поэтому посмотрите, что я имею в виду здесь.Свойство this.name - это то, что изменяется.

//from W3Schools website
function getXHR(){if (window.XMLHttpRequest){return new XMLHttpRequest();}if (window.ActiveXObject){return new ActiveXObject("Microsoft.XMLHTTP");}return null;}

function TestObject()
{
    this.name = "";            //public
    var xhr = null;            //private
    var response = function()  //private
    {
        if(xhr.readyState > 3)
        {
            alert("B: my name is " + this.name);
        }
    }
    this.send = function()     //public
    {
        alert("A: my name is " + this.name);
        if(xhr === null)
        {
            xhr = getXHR();
        }
        var url = "http://google.com";
        xhr.onreadystatechange = response;
        xhr.open("GET", url, true);
        xhr.send(null);          
    }
}
var o = new TestObject();
o.name = "Ice Cube";
o.send();

Результаты:

A: my name is IceCube
B: my name is undefined

Если ответ публичный, это также происходит.Если xhr является публичным, это также происходит.Что-то происходит, так что вызываемая функция ответа не имеет доступа к тем же параметрам.

Ответы [ 3 ]

5 голосов
/ 17 июня 2010

this работает совершенно иначе в JavaScript, чем в некоторых других языках, таких как C # или Java. Значение this внутри функции полностью определяется тем, как функция называется , а не там, где функция определена . Подробнее здесь: Вы должны помнить «это» .

Когда функция вызывается "обычным" способом:

foo();

... тогда в функции this всегда будет ссылаться на глобальный объект (в браузерах глобальный объект - window). В экземпляре объекта нет ничего, что «связывало бы» его с этим объектом. Например:

var obj = {
    name: "Joe",
    speak: function() {
        alert("I'm " + this.name);
    }
};
obj.speak(); // alerts "I'm Joe"
var f = obj.speak;
f();         // alerts "I'm " (this.name is undefined within the call)

Таким образом, чтобы получить обработчик события, вызываемый с правильным «контекстом» (this значение), вы должны предпринять специальные шаги, как описано в посте , связанном выше .

В вашем конкретном случае, поскольку вы уже определяете замыкания для своих функций, вы можете легко обработать его с помощью переменной, по которой ваши замыкания будут закрыты:

function TestObject()
{
    var self;

    // Set up a variable referencing `this`
    self = this;

    // From here on out, use it instead of `this`

    self.name = "";            //public
    var xhr = null;            //private
    var response = function()  //private
    {
        if(xhr.readyState > 3)
        {
            alert("B: my name is " + self.name);
        }
    }
    self.send = function()     //public
    {
        alert("A: my name is " + self.name);
        if(xhr === null)
        {
            xhr = getXHR();
        }
        var url = "http://google.com";
        xhr.onreadystatechange = response;
        xhr.open("GET", url, true);
        xhr.send(null);
    }
}

Подробнее о замыканиях в этой статье , но в основном это работает, потому что переменная self доступна для функций, определенных в конструкторе TestObject. Вы не используете this, поэтому вам не нужно заботиться о том, чтобы обработчики событий вызывались так, чтобы правильно установить this.

Есть причины, по которым вы, возможно, не захотите использовать эти замыкания (влияние на память, если вы создаете много TestObject с, потому что каждый TestObject получает собственную копию каждой функции) , но так как вы уже определили объект, использовать его практически бесплатно. В данном конкретном случае, я предполагаю, что вы не создаете тысячи таких XHR-респондентов.

3 голосов
/ 17 июня 2010

window владеет методом onreadystatechange, поэтому в вашем обратном вызове this относится к window.

Вы можете просто сохранить экземпляр в теле функции TestObject, var that = this и использовать вместо него that.name. Это связывает переменную с вашим обратным вызовом, чтобы она запомнила свой собственный экземпляр.

function getXHR(){if (window.XMLHttpRequest){return new XMLHttpRequest();}if (window.ActiveXObject){return new ActiveXObject("Microsoft.XMLHTTP");}return null;}

function TestObject()
{
    var that = this;
    that.name = "";            //public
    var xhr = null;            //private
    var response = function()  //private
    {
        if(xhr.readyState > 3)
        {
            alert("B: my name is " + that.name);
        }
    }
    this.send = function()     //public
    {
        alert("A: my name is " + that.name);
        if(xhr === null)
        {
            xhr = getXHR();
        }
        var url = "http://google.com";
        xhr.onreadystatechange = response;
        xhr.open("GET", url, true);
        xhr.send(null);          
    }
}
var o = new TestObject();
o.name = "Ice Cube";
o.send();
0 голосов
/ 17 июня 2010

Это потому, что ваша response функция не фактически вызвана для вашего TestObject объекта.

Вы видите, что в JavaScript функции не связаны строго с такими объектами, как вывидел бы в C ++, C #, Java или почти любом другом языке.Вместо этого функции на самом деле являются отдельными объектами, существующими сами по себе, и у них есть это интересное свойство, которое вы можете вызывать любой функцией для любого объекта.

Например:

var obj1 = { Prop1: "Property 1 Value!" };
var obj2 = { Prop2: "Property 2 Value!" };
var myFunc = function() { alert( "Prop1 = " + this.Prop1 + ", Prop2 = " + this.Prop2 ); };

obj1.fn = myFunc; // Assign myFunc to property obj1.fn
obj1.fn();        // Call myFunc with this=obj1

// Similarly for obj2:
obj2.somename = myFunc;
obj2.somename();

Вывод будет:

Prop1 = Property1 Value!, Prop2 = undefined
Prop1 = undefined, Prop2 = Property 2 Value!

Видишь, что я здесь сделал?Функция myFunc существует сама по себе, не привязанная к какому-либо конкретному объекту.И я назначаю его свойствам различных объектов.

И функция - это значение - как, скажем, строка или целое число.Я могу сделать это:

var str = "Some String";
obj1.someprop = str;

И так же легко я могу сделать это:

var myFunc = function() {}
obj1.someotherprop = myFunc;

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

var obj1 = {};
obj1.Prop = "Value";
obj1.Func = function() { alert( this.Prop ); }

obj1.Func();        // Displays "Value"

var f = obj1.Func;
f();                // Displays "undefined"

Возвращаясь к исходной проблеме: ваша проблема в том, что ваша response функция не вызывается для вашего TestObject объекта.Следовательно, вы должны сохранить ссылку на этот объект в локальной переменной и использовать ее вместо this.Вот так:

function TestObject()
{
    this.name = "";            //public
    var xhr = null;            //private

    var _this = this;
    var response = function()  //private
    {
        if(xhr.readyState > 3)
        {
            alert("B: my name is " + _this.name);
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...