Using instance methods as callbacks for event handlers changes the scope of this from "My instance" to "Whatever just called the callback". So my code looks like this
function MyObject() {
this.doSomething = function() {
...
}
var self = this
$('#foobar').bind('click', function(){
self.doSomethng()
// this.doSomething() would not work here
})
}
It works, but is that the best way to do it? It looks strange to me.
This question is not specific to jQuery, but specific to JavaScript in general. The core problem is how to "channel" a variable in embedded functions. This is the example:
var abc = 1; // we want to use this variable in embedded functions
function xyz(){
console.log(abc); // it is available here!
function qwe(){
console.log(abc); // it is available here too!
}
...
};
This technique relies on using a closure. But it doesn't work with this because this is a pseudo variable that may change from scope to scope dynamically:
// we want to use "this" variable in embedded functions
function xyz(){
// "this" is different here!
console.log(this); // not what we wanted!
function qwe(){
// "this" is different here too!
console.log(this); // not what we wanted!
}
...
};
What can we do? Assign it to some variable and use it through the alias:
var abc = this; // we want to use this variable in embedded functions
function xyz(){
// "this" is different here! --- but we don't care!
console.log(abc); // now it is the right object!
function qwe(){
// "this" is different here too! --- but we don't care!
console.log(abc); // it is the right object here too!
}
...
};
this is not unique in this respect: arguments is the other pseudo variable that should be treated the same way — by aliasing.
Yeah, this appears to be a common standard. Some coders use self, others use me. It's used as a reference back to the "real" object as opposed to the event.
It's something that took me a little while to really get, it does look odd at first.
I usually do this right at the top of my object (excuse my demo code - it's more conceptual than anything else and isn't a lesson on excellent coding technique):
function MyObject(){
var me = this;
//Events
Click = onClick; //Allows user to override onClick event with their own
//Event Handlers
onClick = function(args){
me.MyProperty = args; //Reference me, referencing this refers to onClick
...
//Do other stuff
}
}
If you are doing ES2015 or doing type script and ES5 then you can use arrow functions in your code and you don't face that error and this refers to your desired scope in your instance.
this.name = 'test'
myObject.doSomething(data => {
console.log(this.name) // this should print out 'test'
});
As an explanation: In ES2015 arrow functions capture this from their defining scope. Normal function definitions don't do that.
var functionX = function() {
var self = this;
var functionY = function(y) {
// If we call "this" in here, we get a reference to functionY,
// but if we call "self" (defined earlier), we get a reference to function X.
}
}
edit: in spite of, nested functions within an object takes on the global window object rather than the surrounding object.
One solution to this is to bind all your callback to your object with javascript's bind method.
You can do this with a named method,
function MyNamedMethod() {
// You can now call methods on "this" here
}
doCallBack(MyNamedMethod.bind(this));
Or with an anonymous callback
doCallBack(function () {
// You can now call methods on "this" here
}.bind(this));
Doing these instead of resorting to var self = this shows you understand how the binding of this behaves in javascript and doesn't rely on a closure reference.
Also, the fat arrow operator in ES6 basically is the same a calling .bind(this) on an anonymous function:
doCallback( () => {
// You can reference "this" here now
});
I haven't used jQuery, but in a library like Prototype you can bind functions to a specific scope. So with that in mind your code would look like this:
$('#foobar').ready('click', this.doSomething.bind(this));
The bind method returns a new function that calls the original method with the scope you have specified.
Just adding to this that in ES6 because of arrow functions you shouldn't need to do this because they capture the this value.
I think it actually depends on what are you going to do inside your doSomething function. If you are going to access MyObject properties using this keyword then you have to use that. But I think that the following code fragment will also work if you are not doing any special things using object(MyObject) properties.
function doSomething(){
.........
}
$("#foobar").ready('click', function(){
});
Related
I'm trying to access the member variables of a prototype class in JavaScript in an event handler -- something I'd typically use the "this" keyword for (or "that" [copy of this] in the case of event handlers). Needless to say, I'm running into some trouble.
Take, for example, this HTML snippet:
<a id="myLink" href="#">My Link</a>
And this JavaScript code:
function MyClass()
{
this.field = "value"
this.link = document.getElementById("myLink");
this.link.onclick = this.EventMethod;
}
MyClass.prototype.NormalMethod = function()
{
alert(this.field);
}
MyClass.prototype.EventMethod = function(e)
{
alert(this.field);
}
Instantiating a MyClass object and calling NormalMethod works exactly like I expect it to (alert saying "value"), but clicking the link results in an undefined value because the "this" keyword now references the event target (the anchor () HTML element).
I'm new to the prototype JavaScript style, but in the past, with closures, I've simply made a copy of "this" in the constructor:
var that = this;
And then I could access members variables in event methods via the "that" object. That doesn't seem to work with prototype code. Is there another way to achieve this?
Thanks.
You need:
this.link.onclick = this.EventMethod.bind(this);
...'bind' is part of Prototype, and returns a function which calls your method with 'this' set correctly.
Your "that=this" closure idiom is still applicable:
function MyClass()
{
...
var that = this;
this.link.onclick = function() {
return that.EventMethod.apply(that, arguments);
// that.EventMethod() works too here, however
// the above ensures that the function closure
// operates exactly as EventMethod itself does.
};
}
You should try
this.link.onclick = this.EventMethod.bind(this);
As stated above, using bind which is a part of the Prototype library is a clean way to solve this problem. This question is a duplicate of another SO question which is answered here with implementation of the bind method without including the whole prototype library :
https://stackoverflow.com/a/2025839/1180286
Sorry for the rather beginner question. What's the differences of function usage between
$(document).keypress(function() {
//some code
});
and
var somethingElse = function() {
//some code
};
I know the latter is to create a function similar to Java's
public void somethingElse() {
//some code
}
but I always assume the former as anonymous function that act inside a method. Can someone shed me some light regarding this? Thanks.
The first one is a jQuery shortcut to create an event listener.
It's equivalent to:
document.addEventListener('keypress', function() {
// some code
});
More info: http://www.w3schools.com/jsref/met_document_addeventlistener.asp
Now, about named or anonymous functions, what's the difference between
var doSomething = function() {
// some code
};
and this?
function doSomething() {
// some code
}
There's no difference for the developer. Of course there's a difference on memory, but in javascript developers don't have to take care of it.
Actually for the case of an event handler or other techniques that use callback functions, you can pass an anonymous function or a previously declared one, it's exactly the same:
$(document).keypress(doSomething);
or
$(document).keypress(function() {
// some code
});
creates an anon function and passes it to the handler
creates an anonymous function and a variable that references it.
creates a named function - that is hoisted
the hoisted function becomes available to you at any line within your function scope, while the non-hoisted function will be undefined until the line where it is declared runs.
there is no difference between #2 and #3 (other than the hoisting) - some people think that the first one creates an object and the 2nd one is some magical thing, or a global function, but nope - they are both function objects within your scope.
The former is a callback, meaning some instructions that will be executed ONLY as soon as the keypress event in your example is triggered.
Thus, a function's name is not required.
Function expressions, the latter, is mostly used when adding an object's property acting as a method like:
var myObject = {};
myObject.sayHello = function() {
alert("Hello");
}
The behavior of "this" when function bar is called is baffling me. See the code below. Is there any way to arrange for "this" to be a plain old js object instance when bar is called from a click handler, instead of being the html element?
// a class with a method
function foo() {
this.bar(); // when called here, "this" is the foo instance
var barf = this.bar;
barf(); // when called here, "this" is the global object
// when called from a click, "this" is the html element
$("#thing").after($("<div>click me</div>").click(barf));
}
foo.prototype.bar = function() {
alert(this);
}
Welcome to the world of javascript! :D
You have wandered into the realm of javascript scope and closure.
For the short answer:
this.bar()
is executed under the scope of foo, (as this refers to foo)
var barf = this.bar;
barf();
is executed under the global scope.
this.bar basically means:
execute the function pointed by this.bar, under the scope of this (foo).
When you copied this.bar to barf, and run barf. Javascript understood as, run the function pointed by barf, and since there is no this, it just runs in global scope.
To correct this, you can change
barf();
to something like this:
barf.apply(this);
This tells Javascript to bind the scope of this to barf before executing it.
For jquery events, you will need to use an anonymous function, or extend the bind function in prototype to support scoping.
For more info:
Good explanation on scoping
Extending jQuery bind to supportscoping
There's a good explanation on this keyword in JavaScript available at QuirksMode.
You might find this:
Controlling the value of 'this' in a jQuery event
or this:
http://www.learningjquery.com/2007/08/what-is-this
Useful.
Get the book: JavaScript: the Good Parts.
Also, read as much as you can by Douglas Crockford
http://www.crockford.com/javascript/
You may use Function.apply on the function to set what this should refer to:
$("#thing").after($("<div>click me</div>").click(function() {
barf.apply(document); // now this refers to the document
});
This is because this is always the instance that the function is attached to. In the case of an EventHandler it is the class that triggered the event.
You can help your self with an anonymous function like this:
function foo() {
var obj = this;
$("#thing").after($("<div>click me</div>").click(function(){obj.bar();}));
}
foo.prototype.bar = function() {
alert(this);
}
this.bar(); // when called here, "this" is the foo instance
this comment is wrong when foo is used as a normal function, not as a constructor.
here:
foo();//this stands for window
I have tried to figure this out or search for it on google, i can only find how to create objects, not exactly how functions work. If someone could explain to me how encapsulation works.
function myObject() {
this.variable1 = "tst";
this.function1 = function() {
//Now this function works. A 'this' function to a private function is ok
_PrivateFunction1();
//Here is error one, I cannot seem to call methods within the object
//from.
this.function2();
}
this.function2 = function() {
alert("look! function2!");
}
function _PrivateFunction1() {
//This does work though. I am confused.
_PrivateFunction2();
}
function _PrivateFunction2() {
alert("look! PrivateFunction1");
//this does not work.
alert(this.variable1);
}
}
I think I can explain this better if we go in reverse order. First, we define some functions:
function _PrivateFunction1() {
//This does work though. I am confused.
_PrivateFunction2();
}
function _PrivateFunction2() {
alert("look! PrivateFunction1");
//this does not work.
alert(this.variable1);
}
This is pretty normal stuff. The only thing that's weird is that they appear inside another function, but that's perfectly fine. JavaScript has function scope, which means that all variables defined inside a function are defined in a new scope. They do not trample on the global namespace. And functions are first-class objects in JavaScript, which means they can be used just like other data types. They can be nested, passed to functions, returned from functions, etc.
Then we run into some trouble:
function _PrivateFunction2() {
alert("look! PrivateFunction1");
//this does not work.
alert(this.variable1);
}
}
Functions in JavaScript are always executed in some context which is referred to by the this keyword. When you call a function directly (i.e. like this: functionName()) the context in which that function executes is the global window object. So, inside _PrivateFunction2, this.variable1 is equivalent to window.variable1 which is probably not what you meant.
You probably wanted to refer to the current instance of myobject which is what this refers to outside of _PrivateFunction2. You can preserve access to this in an inner scope by storing a reference to it in another variable:
var _this = this;
function _PrivateFunction2() {
alert("look! PrivateFunction1");
//this does not work.
alert(_this.variable1);
}
There's something subtle here you should notice. _PrivateFunction2 has access to the variables defined in its lexical scope, which is why it can access _this. This will be important later.
Next up:
function _PrivateFunction1() {
//This does work though. I am confused.
_PrivateFunction2();
}
This should be the most normal-looking section to you, I would think. There's nothing strange going on here. Just one regular function calling another one. Don't be confused by the fact that these are nested inside myObject. That changes the scope they're in, but not much else.
Next we define some instance variables and methods:
this.variable1 = "tst";
this.function1 = function() {
//Now this function works. A 'this' function to a private function is ok
_PrivateFunction1();
//Here is error one, I cannot seem to call methods within the object
//from.
this.function2();
}
this.function2 = function() {
alert("look! function2!");
}
Here this really does refer to myObject, assuming -- and it's an important assumption -- that myObject was called with the new operator, like this:
var obj = new myObject();
If it had been called like this:
var obj = myObject();
Then this would refer to the window object, just like it did for the functions we saw earlier. The key takeaway is that the value of this is not fixed. It's determined by the way in which the function is called. There are even ways to set it to an arbitrary object explicitly.
The value of this inside this.function1 will also be the current instance of myObject, because it will most likely be used like this:
var obj = new myObject();
obj.function1();
Writing object.method() sets this to object inside method.
So how is this.function1 able to call _PrivateFunction1()? Just as we saw earlier when saving the value of this for use inside a nested function, _PrivateFunction1() is just another object defined in this.function1's lexical scope, so it is available for its use, just as _this was earlier.
And it's because of closure that these private variables are still alive long after myObject has returned.
Footnote: Because failing to use new when instantiating objects breaks things so spectacularly without warning, it's considered good practice to capitalize the names of functions you intend to be used as a constructor. So myObject should really be MyObject.
You have to save a copy of the actual object like here note the var that = this
Tested in chrome and FF
for encapsulation I would prefer module pattern like
var someMethod = function(){
var i,
length,
// public method
public = function(num1, num2){
return num1+num2;
},
//private method
_private = function(){};
//exposing the public method
return{
public:public
}
};
var callPublic = someMethod();
callPublic.public(20, 30);// call the public method and return 50
now , if you try to call that private method like
callPublic._private();//it will return not a function since its not been exposed
There are lot of other pattern to encapsulate your methods but module pattern is most common. Hope ot will help you how to encapsulate data in javascript.
I'm trying to access the member variables of a prototype class in JavaScript in an event handler -- something I'd typically use the "this" keyword for (or "that" [copy of this] in the case of event handlers). Needless to say, I'm running into some trouble.
Take, for example, this HTML snippet:
<a id="myLink" href="#">My Link</a>
And this JavaScript code:
function MyClass()
{
this.field = "value"
this.link = document.getElementById("myLink");
this.link.onclick = this.EventMethod;
}
MyClass.prototype.NormalMethod = function()
{
alert(this.field);
}
MyClass.prototype.EventMethod = function(e)
{
alert(this.field);
}
Instantiating a MyClass object and calling NormalMethod works exactly like I expect it to (alert saying "value"), but clicking the link results in an undefined value because the "this" keyword now references the event target (the anchor () HTML element).
I'm new to the prototype JavaScript style, but in the past, with closures, I've simply made a copy of "this" in the constructor:
var that = this;
And then I could access members variables in event methods via the "that" object. That doesn't seem to work with prototype code. Is there another way to achieve this?
Thanks.
You need:
this.link.onclick = this.EventMethod.bind(this);
...'bind' is part of Prototype, and returns a function which calls your method with 'this' set correctly.
Your "that=this" closure idiom is still applicable:
function MyClass()
{
...
var that = this;
this.link.onclick = function() {
return that.EventMethod.apply(that, arguments);
// that.EventMethod() works too here, however
// the above ensures that the function closure
// operates exactly as EventMethod itself does.
};
}
You should try
this.link.onclick = this.EventMethod.bind(this);
As stated above, using bind which is a part of the Prototype library is a clean way to solve this problem. This question is a duplicate of another SO question which is answered here with implementation of the bind method without including the whole prototype library :
https://stackoverflow.com/a/2025839/1180286