Reputation:
When running this code, the whatever value is first passed in with the constructor Test(callbacks)
becomes the callback that is always called, even in later instantiations of Test
function Test(callbacks) {
if (callbacks) {
if (callbacks.callback) {
this.callback = callbacks.callback;
}
}
this.options.complete = $.proxy(this.options.complete, this);
}
Test.prototype = {
options: {
type: "GET",
complete: function() {
this.callback();
}
},
callback: function() { console.log("OVERRIDE ME"); },
execute: function() {
$.ajax(this.options);
}
};
var eins = {callback: function() {console.log("AAA");}};
var zwei = {callback: function() {console.log("BBB");}};
var A = new Test(eins);
var B = new Test(zwei);
A.execute();
B.execute();
Running this code, every time you will get the output AAA
. How does function() {console.log("AAA");}
become a constant value for the prototype?
Upvotes: 2
Views: 323
Reputation: 35760
It all starts with this line:
this.callback = callbacks.callback;
When you make a call new Test(eins)
, eins
comes in as the callbacks
argument. That line then sets this.callback
(ie. the "callback" property on your new instance of Test) to the callback property of callbacks
, ie. of eins
.
Now, that alone wouldn't affect B. However, there's something tricky:
this.options.complete = $.proxy(this.options.complete, this);
You would think that that would set the "options" property on your Test instance, right? Wrong. The way Javascript works is that if a property isn't defined on your instance (eg. you didn't do this.options = something
) then Javascript will look up the "prototype chain", where it will find the prototype's "options", and set it (not your instance's "options", since your instance doesn't have one).
You can fix all this by changing that line to:
this.options = {complete: $.proxy(this.options.complete, this)};
but of course that would lose your type: "GET",
so either you need to do:
this.options = {type: "GET", complete: $.proxy(this.options.complete, this)};
or you need to base your options off the prototype's:
this.options = {};
for (var key in this.prototype.options) {
this.options[key] = this.prototype.options[key];
}
this.options.complete = $.proxy(this.options.complete, this);
If you happen to be using the (excellent) Underscore library, it even has an extend
function for doing this sort of thing more easily:
this.options = _.extend({}, this.prototype.options,
{complete: $.proxy(this.options.complete, this)});
Or (depending on your style preferences):
this.options = _.extend({}, this.prototype.options);
this.options.complete = $.proxy(this.options.complete, this);
Incidentally, Underscore also has a _.bind
method which is comparable to jQuery's "proxy", so you could also do:
this.options = _.extend({}, this.prototype.options);
this.options.complete = _.bind(this.options.complete, this);
Upvotes: 1
Reputation: 382102
When you do
this.options.complete = $.proxy(this.options.complete, this);
You're replacing the options.complete
function of the prototype by one that will always have this
as context.
The problem is that you don't have a this.options
object proper to this
, but only one shared with all objects having the same prototype.
Upvotes: 0