Javascript Object Properties go to undefined after ajax request returns
if you have an object and set a property for it, you can acce开发者_如何学Pythonss that property in a function called on that object. but if you call a function and do an ajax request such that a different function is called from onreadystatechange, that secondary response function does not have access to the property. Thats a little confusing so see what I mean here. The property this.name is the one that changes.
//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();
Results are:
A: my name is IceCube
B: my name is undefined
If response is public this happens as well. If xhr is public this also happens. Something occurs so that the response function called doesnt have access to the same parameters.
this
works very differently in JavaScript than it does in some other languges, like C# or Java. The value of this
within a function is set entirely by how a function is called, not where the function is defined. More here: You Must Remember 'this'.
When a function is just called in the "normal" way:
foo();
...then within the function, this
will always reference the global object (on browsers, the global object is window
). There's nothing about a function on an object instance that "ties" it to that object. For instance:
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)
So to get an event handler called with the right "context" (this
value), you have to take special steps, as outlined in the post linked above.
In your particular case, since you're already defining closures for your functions, you can readily handle it with a variable that your closures will close over:
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);
}
}
More on closures in this article, but basically, that works because the self
variable is available to the functions defined within the TestObject
constructor. You don't use this
, so you don't worry about making sure that the event handlers get called in a way that sets this
correctly.
There are reasons you may not want to use those closures (memory impact, if you create a lot of TestObject
s, because each and every TestObject
gets its own copy of every function), but since that's already how you've defined the object, there's virtually no cost in making use of them. In this particular case, I'm guessing you're not creating thousands of these XHR responders.
window
owns the onreadystatechange
method so in your callback, this
refers to window
.
You could just save the instance in the function body of TestObject, var that = this
and use that.name
instead. This binds the variable to your callback so it will remember its own instance.
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();
This is because your response
function is not actually called on your TestObject
object.
You see, in JavaScript, functions are not strictly affiliated with objects, like you would see in C++, C#, Java or pretty much any other language. Instead, functions are actually separate objects, existing all by themselves, and they have this interesting property that you can call any function on any object.
For example:
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();
Output would be:
Prop1 = Property1 Value!, Prop2 = undefined
Prop1 = undefined, Prop2 = Property 2 Value!
See what I've done here? The function myFunc
exists all by itself, not attached to any particular object. And I am assigning it to properties of different objects.
And function is a value - just like, say, a string or an int. I can do this:
var str = "Some String";
obj1.someprop = str;
And just as easily I can do this:
var myFunc = function() {}
obj1.someotherprop = myFunc;
Now, when value of some property of some object is a function, then you can call that function, and it will be called on that object - meaning that this
will be a reference to that object. But if you call that same function without using the obj1.
prefix, it will not be called on that object. Example:
var obj1 = {};
obj1.Prop = "Value";
obj1.Func = function() { alert( this.Prop ); }
obj1.Func(); // Displays "Value"
var f = obj1.Func;
f(); // Displays "undefined"
Going back to your original problem: your problem is that your response
function is not getting called on your TestObject
object. Therefore, you must save a reference to that object in a local variable and use it instead of this
. Like so:
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);
}
}
精彩评论