I am trying to call instance method inside event handler of another instance method but I am getting function undefined, I assume this is because in the event handler "this" refers to DOM element rather then the instance:
function MyObject(something) {
this.something = something;
this.value = 'abc';
}
MyObject.prototype.Init = function() {
$(this.something).click(function() {
this.DoSomething();
});
};
MyObject.prototype.DoSomething = function() {
//do something
};
Is there way to get "this" to point to instance?
Define another variable whose name is something other than this and assign it the context you wish to refer to in your inner function:
MyObject.prototype.Init = function() {
var scope = this;
$(this.something).click(function() {
scope.DoSomething();
});
};
Related
I have a class-like function
var myapp = function() {
this.method = function() {
//Do something...
}
}
To reference myapp from within methods, the first line in the myapp function is
var self = this;
So a method in myapp can reference the "class" safely
this.anothermethod = function() {
self.method();
}
The full code:
var myapp = function() {
var self = this;
this.dosomething = function(Callback) {
Callback();
}
this.anothermethod = function() {
//Pass a callback ("self" is required here)...
this.dosomething(function() {
self.complete();
)};
}
this.complete = function() {
console.log('All done!');
}
}
My question is: can I assign var self = this; from outside the declaration of myapp? I don't want to set self every single time I write a "class".
Kind of like this:
var library = function() {
this.loadclass = function(Name) {
var tempclass = window[Name];
library[Name] = new tempclass();
library[Name].self = library[Name];
}
}
var myapp = new library();
myapp.loadclass('myapp');
myapp.myapp.dosomething();
It doesn't work as expected. self equals window for some reason.
I know it's a little abnormal programming, but can it be done?
Note about using self: I remember why I started using it. I wanted to reference the base class (this) from within callbacks inside methods. As soon as you try to use this within a function within a method, it then references the method, not the base class.
Unless you are detaching the methods from the object and calling them as plain functions, you don't need a self variable at all. The method can reach its object using the this keyword:
var myapp = function() {
this.method = function() {
//Do something...
}
this.anothermethod = function() {
this.method();
}
}
No, you can't really; not the way you're creating objects at least.
You can sort of do this, by enumerating all the functions on the object and binding them to the object itself. Something like this:
Object.keys(obj)
.filter(function(n) { return typeof obj[n] == "function" })
.forEach(function(n) { obj[n] = obj[n].bind(obj) })
This function will go over the public, enumerable properties of obj and make sure that any functions on it are bound to obj; i.e. this is now bound to obj.
A primer on this
When you call new, this within the constructor gets bound to the newly created object. If you do need a reference to this as it was bound at constructor time, you do need to keep away a reference to it.
Functions in JavaScript are bound to wherever it is called. Here's an example:
var foo = new function() {
this.bar = function() {
return 'bar'
}
this.baz = function() {
return this.bar()
}
}
console.log(foo.bar()) // bar
console.log(foo.baz()) // bar
var bar = function() {
return "window"
}
var baz = foo.baz
console.log(baz()) // window
When we call foo.baz() it'll look to foo for the implementation of bar, but when calling foo.baz through a "detached" reference, it'll look to whatever the global object is (in this case the browser window object) and call bar from there. Because we defined bar in the global context, it then returns window.
The practice of assign a variable called self is so that it doesn't matter how you call your methods, because you always reference the this at the time of creation through the self variable. You don't have to write things this way, but then you should understand that references to this may change under your feet.
I have an object. It initializes a button to alert "Hello!" when it is clicked. Why won't this work?
jsfiddle: http://jsfiddle.net/kUT52/1/
HTML
<button>Test</button>
JS
var MyObject = {
testValue: "Hello!",
testFunction: function() {
alert(this.testValue);
},
init: function(button) {
button.click(this.testFunction());
}
}
$(document).ready(function(){
var buttonInstance = new MyObject();
var button = $('button');
buttonInstance.init(button);
});
Whenever you put () behind a function reference, you are executing the function. You have to pass the function reference to .click, not what the function returns (unless the function returns a function that you want to use as event handler).
Example:
button.click(this.testFunction);
But now you have another problem: Inside the function, this will refer to the DOM element and not to the object, so accessing this.testValue will return undefined.
You can use jQuery's $.proxy function to fix this:
button.click($.proxy(this.testFunction, this));
Now this will refer to the object, and you can get a reference to the clicked element via event.target.
For two reasons:
You are using testFunction() instead of testFunction when you bind the event, so you will be calling the function and binding the (undefined) return value.
When you use a method as a function, it's no longer attached to the object, so the context will not be the object when the callback is called, but the global window object.
Use the proxy method to make a function that calls the method with the right context:
button.click($.proxy(this.testFunction, this));
That's an object literal, and you'd normally use it like so :
var MyObject = {
testValue: "Hello!",
testFunction: function() {
alert(MyObject.testValue);
},
init: function(button) {
button.on('click', this.testFunction);
}
}
var button = $('button');
MyObject.init(button);
FIDDLE
or passing the object:
var MyObject = {
testValue: "Hello!",
testFunction: function(e) {
alert(e.data.obj.testValue);
},
init: function(button) {
button.on('click', {obj: this}, this.testFunction);
}
}
var button = $('button');
MyObject.init(button);
FIDDLE
At the moment I came this far.
function Class() {
var privateMethod = function () {
return 'private'
}
this.publicMethod = function () {
return 'public'
}
var _constructor = function () {
$(document).on('click', _onClick)
}
var _onClick = function () {
// My error is `this`, focus now on the click event, but I need the object itself
console.log(privateMethod())
console.log(this.publicMethod())
}
_constructor()
}
$(document).ready(init)
function init() {
new Class()
}
The problem is that, in the click event, I am unable to call publicMethod.
I am able to call the private method.
How can I achieve this?
The problem is that, in your handler you've lost your context (this no longer means your instance of Class, it instead means the object that triggered your event. You need to create a closure scoped version of this to hold onto that context.
var self = this;
var _onClick = function () {
// My error is `this`, focus now on the click event, but I need the object itself
console.log(privateMethod())
console.log(self.publicMethod())
}
You have a scope issue, this in the onclick is pointing to a different object than what you expect. In your case it is the document
var that = this;
var _onClick = function () {
// My error is `this`, focus now on the click event, but I need the object itself
console.log(privateMethod())
console.log(that.publicMethod())
}
Running Example
I am currently creating a dialog within a user-define class:
$("<div>").dialog(buttons: {
'one': function () {
$(this).dialog('close').dialog('destroy');
}
});
The above works fine, however, this no longer refers to the class instance in the above function. I can get around this with $.proxy:
...buttons: {
'one': $.proxy(function () {
this.doWork();
}, this)
Then, I can call class methods when the dialog button is clicked.
However, I still need to call .dialog('close').dialog('destroy') on the dialog element itself. After redefining this with $.proxy, how can I access that element in the button callback? e.target refers to the button itself.
I also realize I can do something like this:
var obj = this;
...buttons: {
obj.doWork();
but I'm looking for a way around that.
I'm not sure why you want to avoid var obj = this; inside the class's scope, but the only other way would be with a self-invoking closure which does essentially the same thing. In order to have a reference to both contexts, you need to store the class reference in a different variable.
With closure:
function MyClass() {
this.createDialog = function () {
$("<div>").dialog({
buttons: {
"one": function (self) {
return function (e) {
self.doWork();
$(this).dialog("close").dialog("destroy");
};
}(this)
}
});
};
this.doWork = function () {
// do work
};
}
$(function () {
var obj = new MyClass();
$(".createDialog").click(function () {
obj.createDialog();
});
});
jsFiddle: http://jsfiddle.net/ar4ZL/
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.