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.
Related
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.
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();
I have wrapped (client-side) socket.io in a prototype class:
Chat.Client = Class.create();
Chat.Client.prototype = {
initialize: function() {
...
this.socket.on('message', this.on_message);
...
},
on_message: function(data) {
this.add_chat_message(data.something);
}
do_something: function(something) {
...
}
This does not work because 'this' in on_message will be 'SocketNamespace'. Traditionally I would work around this by just passing 'this' into the callback as an additional parameter, but because I am using socket.io, I cannot simply do this.
How can I solve this?
You can wrap it in another function:
initialize: function() {
// ...
var client = this;
this.socket.on('message', function(data) { client.on_message(data); });
In newer browsers (or Node.js) you can alternatively use a function on the Function prototype called "bind":
this.socket.on('message', this.on_message.bind(this));
The "bind" function returns a function that, when called, will force the original function ("on_message") to be invoked with the given value as the this value.
i have JavaScript components, that has following architecture:
var MyComponent = function(params)
{
setup(params);
this.doSomething()
{
// doing something
};
function setup(params)
{
// Setup
// Interaction logic
var _this = this; // "this" points to DOMWindow, not to created object
$(".some-element").click(function(){
_this.doSomething(); // it craches here, because of above
});
}
};
When something, being controlled by interaction logic, happens, sometimes i must forward execution to "public" methods of component.
In this situation, i have a problem with "this" pointer.
Sample code demonstrates it:
var Item = function()
{
this.say = function()
{
alert("hello");
};
this.sayInternal = function()
{
_sayInternal();
};
function _sayInternal()
{
this.say();
};
};
To test it,
Create an object:
var o = new Item();
This works fine:
o.say(); // alerts "hello"
This crashes:
o.sayInternal();
I get an error:
TypeError: Result of expression 'this.say' [undefined] is not a function.
I think, such a behaviour takes place, because _sayInternal() function is declared (and not assigned to object, like "this.say = function()"). This way, it is shared across all created objects and acts like a static function in C++.
Is this true ?
No, sayInternal is not shared between created objects. But you are right, the created objects don't have access to sayInternal as it is not assigned to them. This function is only local to the constructor function.
this always refers to the context a function is invoked in. If you call it like func(), then this refers to the global object (which is window in browser). If you set the function as property of an object and call it with obj.func(), then this will refer to obj.
If you assign a "bound" function to a variable and call it:
var method = obj.func;
method();
then this will again refer to the global object. In JavaScript, functions are like any other value, they don't have a special relationship to the object they are assigned to.
You can explicitly set the context with call or apply:
var MyComponent = function(params)
{
setup.call(this, params); // <- using `call`
this.doSomething()
{
// doing something
};
function setup(params)
{
// Setup
// Interaction logic
var _this = this; // "this" to new created object
$(".some-element").click(function(){
_this.doSomething();
});
}
};
or in you other example:
var Item = function()
{
this.say = function()
{
alert("hello");
};
this.sayInternal = function()
{
_sayInternal.call(this);
};
function _sayInternal()
{
this.say();
};
};
That said, this approach to assign functions to objects is not good, because every instance will have its own this.sayInternal function. So for the Item code above, every creation of an instance involves creating three functions too, which is a waste of memory.
Making use of prototype inheritance would be a better way:
var Item = function() {
};
Item.prototype = (function() {
function _sayInternal() {
this.say();
};
return {
say: function() {
alert("hello");
},
sayInternal: function(){
_sayInternal.call(this);
}
}
}());
This way, _sayInternal is only created once and all instances inherit (refer to) the prototype, so say and sayInternal also exist only once. The "trick" with the immediate function makes _sayInternal only accessible by say and sayInternal.
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.