Basically, I use a meta-class framework called Joose for Javascript that allows me to make use of a more elegant class syntax - but I don't know how I might go about referencing the scope of the object from within deeper methods of the class declaration. I also use require.js for dependemcy management...
Here's an example class definition:
define([
'jquery',
'handlebars',
], function($, Handlebars){
var MyClass = Class("MyClass", {
//inheritance
isa: SuperClass,
//instance vars
has: {
hello:{
is: 'r',
init: 'Hi There!',
},
someVarToBeSetUsingAjax:{
is: 'rw',
init: false,
},
},
//methods
methods: {
init: function () {
var self = this;
self.getAjaxVar();
},
getAjaxVar: function() {
var self = this;
//HOW CAN I REFERENCE 'self' THROUGHOUT MY OBJECT?
$.get('ajax/test.html', function(response) {
self.someVarToBeSetUsingAjax = response.value;
});
},
//lots more methods...
}
});
return MyClass;
});
Ok, so my issue is - in the AJAX function I have to write var self = this to get my object into the scope of the AJAX call - no problem. But, I find myself doing this for almost every single method in my class declaration! How can I reference self in all of the methods in a clean and efficient way? I know you can use scope in AJAX by setting a parameter, assume it's not just AJAX but other functions that close the scope to the outside.
Thanks.
Everytime you nest a function, you have to think about this
. But if you dont nest a function, or that function doesn't use this
you don't need to think about it.
init: function () {
var self = this;
self.getAjaxVar();
},
So in this case it's not necessary. This is exactly the same:
init: function () {
this.getAjaxVar();
},
But here:
getAjaxVar: function() {
var self = this;
$.get('ajax/test.html', function(response) {
self.someVarToBeSetUsingAjax = response.value;
});
},
You create an inner function, and you want a reference to the original value of this
, so you do have to alias this
to self
to make it accessible.
There isn't a way to fix this
to a value from everywhere in your class.
That said, you do have some options.
Function.prototype.bind()
can help.
var func = function() { return this.name };
var obj = { name: 'Bob' };
var boundFunc = func.bind(obj);
boundFunc(); // 'Bob'
bind
will return a new function with this
always set to a specific object.
So:
getAjaxVar: function() {
$.get('ajax/test.html', function(response) {
this.someVarToBeSetUsingAjax = response.value;
}.bind(this));
},
Note this isn't supported in all browsers, you may need a shim for the old ones.
Or just get used to self = this
.
I want to give a minor nod to coffeescript as well, because it supports declaration of functions that dont change the context when run.
obj = {
name: "bob"
sayHello: ->
doSomeAjax({
success: =>
alert "successfully made " + this.name + " say hello!"
})
}
obj.sayHello()
->
makes a normal function. But the fat arrow =>
will instead preserve the value of this
inside and outside the function. It's very very handy in callbacks within instance methods. When compiled to JS, it basically does a self = this
alias for you, using self
within the inner function everytime to reference this
. It's pretty slick.
In plain JS though, the most common pattern is simply self = this
, stick to it.
The answer is to use this
. You only need to create a closure (which you do with the var self = this;
when you have a function which will be called external to the object. (as you do here with the return from the ajax call).
There is no other way to create a closure.
To be clear (since some will jump on any slight technical hand waving), you don't "need" to create a closure. But I think you should -- JavaScript was designed to work with closures, they work well and they are well understood by other JavaScript programmers.
I find myself doing this for almost every single method in my class declaration! How can I reference self in all of the methods in a clean and efficient way?
You cannot. The value of self
, the instance referenced by this
, is only known on calling the method.
You only could work around that and use one common self
variable if you declared that in the constructor (your init
method?) and created all the callback functions there as well. Might not be the most memory-efficient method, though:
return Class("MyClass", {
// …
methods: {
init: function () {
var self = this;
this.ajaxVarCallback = function(response) {
self.someVarToBeSetUsingAjax = response.value;
};
// and other functions that use "self"
this.getAjaxVar();
},
getAjaxVar: function() {
$.get('ajax/test.html', this.ajaxVarCallback);
},
// …
}
});
Apart from always keeping all instances of MyClass in scope (or as a global variable), I don't see any solutions. However, you could do away with having to declare self
everywhere by using Function.prototype.bind
:
$.get('ajax/test.html', function(response) {
this.someVarToBeSetUsingAjax = response.value;
}.bind(this));
This isn't limited to $.get(), you can use it everywhere you're using callback functions.