OO JavaScript - Avoiding self = this - javascript

Does anyone know of a way to get around declaring var self = this when using JavaScript in an OO fashion? I see it quite often and was curious if its just something you have to do, or if there really is a way (perhaps a class library?) that lets you get around it? I do realize why it is necessary (this has function scope). But you never know what clever ways may be out there..
For example, I usually code my "classes" like this in JS:
function MyClass() {
}
MyClass.prototype = {
firstFunction: function() {
var self = this;
$.ajax({
...
success: function() {
self.someFunctionCall();
}
});
},
secondFunction: function() {
var self = this;
window.setTimeout(function() {
self.someOtherFunction();
}, 1000);
}
};

In your first function you can do this...
$.ajax({
context: this,
success: function() {
this.someFunctionCall();
}
});
In the second one, you can do this, though you'll need to shim .bind() in older browsers...
window.setTimeout(function() {
this.someOtherFunction();
}.bind(this), 1000);
With jQuery, you could also do this...
window.setTimeout($.proxy(function() {
this.someOtherFunction();
}, this), 1000);

No, you need to do this if you want to refer to this in a different context (such as a callback) since otherwise it will be reassigned to another object such as window.
By the way, self is a python convention - in JavaScript people generally use the convention that = this. But it is just a matter of personal taste.

ES5 added the standard method called bind which allows you to bind the this of a function as well as the first n number of parameters. In the example above, you can avoid using self by calling bind.
$.ajax({
...
success: function() {
this.someFunctionCall();
}.bind(this);
});
For non-ES5 browsers you can use a shim for it such as the one found here: https://github.com/kriskowal/es5-shim
As an asside, I would avoid using self in your coding pattern because self is defined as a global variable that is equal to window which is the global scope. In other words, if you accidentally forget to define self you will silently get the global scope as the value instead of an exception. If you use that instead, you will get an exception (unless someone above you defined it).

Some javascript frameworks have their own event handling mechanisms that allow you to set context for the handler function. This way, instead of using self = this, you can simply specify this as the context.
Other possibility that comes to my mind is to pass the context in somewhere in global scope. Like
function MyClass() {
MyClass.instances.push(this);
}
MyClass.instances = new Array();
MyClass.getInstanceBySomeRelevantParameter = function(param) {
for (var i = 0; i < MyClass.instances.length; i++)
if (condition(param))
return MyClass.instances[i];
}
...
success: function(event) {
MyClass.getInstanceBySomeRelevantParameter(event).someFunctionCall();
}

You may always bind your methods to this and then use it as follows:
function MyClass() {
}
MyClass.prototype = {
firstFunction: function() {
var funct = someFunctionCall.bind(this);
$.ajax({
...
success: function() {
funct();
}
});
},
secondFunction: function() {
var funct = someOtherFunction.bind(this);
window.setTimeout(function() {
funct();
}, 1000);
}
};
For properties just assign them to another variable.

I fooled around on JSFiddle, and came up with the below. It does assume that you are using a top level namespace. This makes it so you only need to declare self once (at the bottom). I wrapped the class in an anonymous function so self wouldn't have a global scope. The fiddle is: http://jsfiddle.net/bdicasa/yu4vs/
var App = {};
(function() {
App.MyClass = function() { }
App.MyClass.prototype = {
firstFunction: function() {
console.log('in first function');
console.log(self === this); // true
},
secondFunction: function() {
window.setTimeout(function() {
self.firstFunction();
console.log(self === this); // false
}, 100);
}
};
var self = App.MyClass.prototype;
})();
var myClass = new App.MyClass();
myClass.secondFunction();

Related

Set variable and pass to function or re-set variable?

Which is best? Set a variable and pass it into the call, or set the var again every time. I want self = this (i.e. the Slider obj) for consistency.
EDIT for answers: I know I can use this but I work with other team members and have agreed for consistency to use self when referencing the object at all times.
So is this better:
Slider = {
init: function() {
var self = this,
foo = $('.someElement');
self.bindEvent(self);
},
bindEvent: function(self) {
self.foo.on('click', self.wasClicked(self));
},
wasClicked: function(e, self) {
e.doSomething();
self.done();
}
};
Or this:
Slider = {
init: function() {
var self = this,
foo = $('.someElement');
self.bindEvent();
},
bindEvent: function() {
var self = this;
self.foo.on('click', self.wasClicked);
},
wasClicked: function(e) {
var self = Slider;
e.doSomething();
self.done();
}
};
Your code is more reusable if you utilize an argument for your binding target, as per your first example. For instance:
bindEvent: function(target) {
target.onclick = function() { alert(target + " was clicked"); };
}
bindEvent() can now be called by other functions, and target something other than self. While in your current Slider object it only needs to be called by init(), in the future you may end up adding functionality to Slider that would be benefit from the added reusability of this function. At that point in time you can avoid rewriting the function, or worse yet creating an essentially duplicate function for the new usecase.
In situations where self is not the target, merely a reference to the parent, either example is fine. If your function already has a lengthy list of arguments it accepts, setting self inside the function instead of passing it as an argument is often a good idea. Lots of arguments make it harder to understand, refactor, and test, code.
You only need self in wasClicked, in the other cases use this:
Slider = {
init: function() {
foo = $('.someElement');
this.bindEvent();
},
bindEvent: function() {
this.foo.on('click', this.wasClicked);
},
wasClicked: function(e) {
var self = Slider;
e.doSomething;
self.done();
}
}
Slider.init();
Regarding your edit: in the first option, the event handler won't work, because you're calling it right away instead of passing a reference. You'd have to modify the method to return a function.

Javascript scope referencing outer object

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.

Prototype Pattern and "this"

I'm trying to create a client-side api for a web control using the Prototype pattern. However I want to make life easier by not having to manage "this".
This is some sample code (i have commented the problematic line):
MyObject = function ()
{
MyObject.initializeBase(this);
this._someProperty = null;
};
MyObject.prototype = {
initialize: function()
{
// Init
},
get_someProperty: function()
{
return this._someProperty;
},
set_someProperty: function(value)
{
this._someProperty = value;
},
doSomething: function ()
{
$('.some-class').each(function ()
{
$(this).click(this.doClick); // this.doClick is wrong
});
},
doClick: function ()
{
alert('Hello World');
}
};
Normally, using the revealing module pattern I would declare a private variable:
var that = this;
Can I do something similar with the Prototype pattern?
You can do the exact same thing you are used to, just do it within the doSomething method:
doSomething: function ()
{
var instance = this;
$('.some-class').each(function ()
{
$(this).click(instance.doClick);
});
},
This approach has nothing to with prototype or not, it's just how to manage context with nested functions. So when a function on a prototype (method) has nested functions within in, you may have to preserve the context this at any of those level if you want to access it in a nested scope.
ES5's Function.prototype.bind() might be an option for you. You could go like
doSomething: function ()
{
$('.some-class').each(function(_, node)
{
$(node).click(this.doClick); // this.doClick is right
}.bind(this));
},
Now, we proxied each event handler by invoking .bind() and as a result, we call it in the context of the prototype object. The caveat here is, you no longer have this referencing the actuall DOM node, so we need to use the passed in arguments from jQuery instead.

Am I using Javascript call-backs appropriately (object-oriented style)?

I have the following code example to use an object that receives the action from the callback. Doesn't seem like this is a good design pattern. Or is it?
When setTimeOut() fires on the function after 1 second, it uses the objInstance global variable (DOM scope) to access the ClassExample object instance. Can someone recommend a better way to utilize callbacks within an object oriented design?
The whole idea is so I can use the callback to update data within my object instance (increment a variable for example).
function ClassExample{
this.initiate = function() {
setTimeOut(objInstance.afterTimeOut,1000); //using the objects global handle
}
this.afterTimeOut = function() {
alert("Received!");
}
}
var objInstance = new ClassExample(); //instance
objInstance.initiate();
No, you're not. You'll want to do this:
this.initiate = function() {
setTimeOut(objInstance.afterTimeOut,1000); //using the objects global handle
}
Now, if "afterTimeout" needs the proper object context, you could do this:
this.initiate = function() {
var instance = this;
setTimeout(function() { instance.afterTimeOut(); }, 1000);
}
OK well you changed the question considerably with that little edit :-) If I were you, I'd just do this (like my original second example):
this.initiate = function() {
var instance = this;
setTimeout(function() { instance.afterTimeOut(); }, 1000);
}
Then you don't need any ugly global variables around at all.
edit — Stackoverflow user #Christoph comments that this isn't particularly pretty. One thing that might help would be to use a "bind" facility, as provided by newer browsers natively (as a method on the Function prototype) or by some libraries (Prototype or Functional for example). What "bind" lets you do is create a little wrapper function like I've got above:
this.initiate = function() {
setTimeout(this.afterTimeOut.bind(this), 1000);
}
That call to "bind" returns a function that is effectively the same sort of thing as the little wrapper I coded explicitly in the example.
function ClassExample{
this.afterTimeOut = function() {
alert("Received!");
}; // Don't forget these
setTimeOut(afterTimeOut, 1000); // Don't use () if you're passing the function as an argument
}
var objInstance = new ClassExample(); //instance
That way you don't need the initiate() method.
If you really want the initiate() method, I'd do it like this:
function ClassExample{
var self = this;
self.afterTimeOut = function() {
alert("Received!");
};
self.initiate = function() {
setTimeOut(self.afterTimeOut, 1000);
};
}
var objInstance = new ClassExample(); //instance
objInstance.initiate();
This is how I'd do it to allow timer reuse and minimize the number of closures:
function Timer(timeout, callback) {
this.timeout = timeout;
this.callback = callback;
}
Timer.prototype.run = function(thisArg /*, args... */) {
var argArray = Array.prototype.slice.call(arguments, 1);
var timer = this;
setTimeout(function() {
timer.callback.apply(thisArg, argArray);
}, timer.timeout);
};
var timer = new Timer(1000, alert);
timer.run(null, 'timer fired!');
And just for fun, a golfed version which is functionally equivalent, but replaces the object with a closure:
function delay(func, timeout) {
return function() {
var self = this, args = arguments;
setTimeout(function() { func.apply(self, args); }, timeout);
};
}
delay(alert, 1000).call(null, 'timer fired!');
You are right it is not the optimal way of doing what you are aiming for. however i have to wonder why you need to break the callstack as part of the initiation, it seems very academic.
apart from that if i had to do that, i'd probably use a closure like so:
function ClassExample{
this.initiate = function() {
setTimeOut((function(self) { return function() { self.afterTimeout();}})(this),1000); //using the objects global handle
}
this.afterTimeOut = function() {
alert("Received!");
}
}
var objInstance = new ClassExample(); //instance
objInstance.initiate()
this.initiate = function() {
var instance = this;
setTimeOut(function() {
instance.afterTimeOut();
}, 1000);
};
By saving this to a local variable, you can avoid using the global handle at all. Also this prevent the afterTimeout() from losing it's this.
Building on Znarkus answer...
I really don't know in which environment his code is running but for me the first approach just do not works. I got: 'ReferenceError: afterTimeOut is not defined'...
The second one, nevertheless, is really cool... I just changed setTimeOut for setTimeout (using lowercase 'o') and included parenthesis after the class name definition turning the first line of code into 'function ClassExample(){'; solved my problem.
My snippet of example code:
Oop with private behaviour, intern callback calling and etc.
function MyTry (name){
// keep this object pointer... that's the trick!
var self = this;
// create private variable
var d = new Date()toJSON().slice(0, 10);
// create a private function
function getName(){return name}
// create public access method
self.hello = function(){alert('Hello '+getName()+'!\nToday is: '+d)}
// note instance method hello passed as a callback function!
self.initiate = function(){setTimeout(self.hello, 3000)}
}

Overwritten "this" variable problem or how to call a member function?

I have this class where I am using a combination of jQuery and Prototype:
var MyClass = Class.create({
initElements: function(sumEl) {
this.sumEl = sumEl;
sumEl.keyup(this.updateSumHandler);
},
updateSumHandler: function(event) {
// Throws error here: "this.updateSum is not a function"
this.updateSum();
},
updateSum: function() {
// does something here
}
});
How can I call this.updateSum() after all?
You need to use closures.
initElements: function(sumEl) {
this.sumEl = sumEl;
var ref = this;
sumEl.keyup( function(){ref.updateSumHandler();});
},
Totally untested suggestion:
sumEl.keyup(this.updateSumHandler.bind(this));
.bind() gives back a new function where the first parameter of bind is closured for you as the function's this context.
It can also closure parameters, check out the documentation.
To me, Function.bind() is the single best function ever written in JavaScript :)
DOMEvent handlers are traditionally called with the elements they're registered to as context / "this". This is what jQuery does, too.
The easiest option for you would be to use jQuery's ability to handle event data
var MyClass = Class.create({
initElements: function(sumEl) {
this.sumEl = sumEl;
sumEl.bind("keyup", this, this.updateSumHandler);
},
updateSumHandler: function(event) {
// event.data is the initial this
// call updateSum with correct context
event.data.updateSum.call(event.data);
},
updateSum: function() {
// does something here
}
});
The other possibility is to use closures to define the updateHandler inside the constructor
var MyClass = Class.create({
initElements: function(sumEl) {
this.sumEl = sumEl;
// save this as that so we can access it from the anonymous function
var that = this;
sumEl.keyup(function()
{
that.updateSum();
});
},
updateSum: function() {
// does something here
}
});
This is a working example what one of the other answers tried to do. It works because the anonymous function can always access the variables in the surrounding function -- but it only works if the function is really defined in the function that has "that" as local variable.
It is the famous Javascript idiom you need to use in initElements function:
var that = this;
Later in your handler just refer to that instead of this:
var MyClass = Class.create({
initElements: function(sumEl) {
this.sumEl = sumEl;
var that = this;
sumEl.keyup(this.updateSumHandler);
},
updateSumHandler: function(event) {
that.updateSum();
},
updateSum: function() {
// does something here
}
});
It was covered in great detail in talk by Stuart Langridge on Javascript closures at Fronteers 2008 conference.

Categories