Say I have a set of js that looks like this:
function a() {
this.meow = 0;
var go = setTimeout(function() {
this.parent.meow++;
}, 500);
}
var woof = new a();
Why does woof.meow not increment and if I am referencing it wrong then why does this work:
(function() {
this.meow = 'woof';
var go = setTimeout(function() {
alert(this.parent.meow);
},500);
return true;
})();
and even more confusingly then why doesn't this work:
(function() {
this.meow = 0;
var go = setTimeout(function() {
alert(this.parent.meow++);
},500);
return true;
})();
parent has no special meaning in JavaScript (although it does on browsers). In your timeout function, this does not refer to your a instance, it refers to the window object. It happens that the window object has a property called parent, but that parent probably doesn't have a property called meow. The reason this is window in your timeout function is that in JavaScript, this is defined entirely by how functions are called, not where they're defined. See links below.
Since your timeout function is a closure over the context of the call to new a, you can do this:
function a() {
var self = this;
this.meow = 0;
var go = setTimeout(function() {
self.meow++;
}, 500);
}
What that does is set a variable called self to the value of this within the call to a. The timeout function closes over self and so can use self.meow.
if I am referencing it wrong then why does this work
The code you've quoted doesn't work (live example), but I suspect you saw something similar to it work because in that code, this references the window object, and it's pretty easy to end up calling a function such that this references window. So meow wold have ended up being a global variable (all properties you put on window are globals).
More on closures and this (on my blog):
Closures are not complicated
Mythical methods
You must remember this
T.J. Crowder is right, alternative:
function a() {
var meow = 0;
var go = setTimeout(function() {
meow++;
}, 500);
}
Related
I have defined an API object:
function API() {
var self = this;
return {
getRandomArticle: function() {
$.getJSON("http://en.wikipedia.org/w/api.php?action=query&generator=random&grnnamespace=0&prop=extracts&exchars=50000&format=json&callback=?", function (data) {
for(var id in data.query.pages) {
console.log(data.query.pages[id].extract);
}
});
},
repeatAPICall: function() {
self.getRandomArticle();
console.log(self);
setTimeout(self.repeatAPICall, 5000);
}
}
}
And then I instantiated the API object with window.test = new API();.
When I head over to Chrome Dev tools and call window.test.repeatAPICall(), it works once, then it fails and says TypeError: Object #<API> has no method 'getRandomArticle'
I suspect that somehow the recursive call is behaving differently than I intended, what am I doing wrong?
Working code:
function API() {
var self = this;
self.getRandomArticle = function() {
$.getJSON("http://en.wikipedia.org/w/api.php?action=query&generator=random&grnnamespace=0&prop=extracts&exchars=50000&format=json&callback=?", function (data) {
for(var id in data.query.pages) {
console.log(data.query.pages[id].extract);
}
});
},
self.repeatAPICall = function() {
self.getRandomArticle();
console.log(self);
setTimeout(self.repeatAPICall, 5000);
}
return this;
}
window.test = new API();
Now you've fixed "self" vs. "this" the next change is to use
self.getRandomArticle= ...
self.repeatAPICall=...
and then just return self/this. That should work. Right now, you have two objects - this and the one you return.
Your main issue is the passing of this.repeatAPICall into setTimeout. When you call a method in JavaScript, the this keyword points to the object that called it:
var something = {
foo : function(){
return this;
}
};
something.foo(); //=> something
However, if you assign the function to a different variable, the context changes (to the global window object):
var something = {
foo : function(){
return this;
}
};
something.foo(); //=> something
var bar = something.foo;
bar(); //=> window
This is what's happening above; you're passing a reference to the function to setTimeout, which is then losing the correct context.
Instead, you need to pass in a function which keeps the context; you could use the self = this statement like so:
repeatAPICall: function() {
self = this;
self.getRandomArticle();
setTimeout(function(){
self.repeatAPICall();
}, 5000);
This creates anonymous function which remembers the state of the self object (this is how JavaScript variable scope works). When that function gets called, it can then call repeatAPICall as a method on that object, rather than as a function with no context.
The accepted answer avoids having to do this (each method can access self), but hopefully this explains why it wasn't working.
Here's a sample of a simple Javascript class with a public and private method (fiddle: http://jsfiddle.net/gY4mh/).
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction();
}
}
ex = new Example;
ex.publicFunction();
Calling the private function from the public one results in "this" being the window object. How should I ensure my private methods are called with the class context and not window? Would this be undesirable?
Using closure. Basically any variable declared in function, remains available to functions inside that function :
var Example = (function() {
function Example() {
var self = this; // variable in function Example
function privateFunction() {
// The variable self is available to this function even after Example returns.
console.log(self);
}
self.publicFunction = function() {
privateFunction();
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Another approach is to use "apply" to explicitly set what the methods "this" should be bound to.
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.apply(foo, null);
// => foo
Yet another approach is to use "call":
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.call(foo, null);
// => foo
both "apply" and "call" take the object that you want to bind "this" to as the first argument and an array of arguments to pass in to the method you are calling as the second arg.
It is worth understanding how the value of this in javascript is determined in addition to just having someone tell you a code fix. In javascript, this is determined the following ways:
If you call a function via an object property as in object.method(), then this will be set to the object inside the method.
If you call a function directly without any object reference such as function(), then this will be set to either the global object (window in a browser) or in strict mode, it will be set to undefined.
If you create a new object with the new operator, then the constructor function for that object will be called with the value of this set to the newly created object instance. You can think of this as the same as item 1 above, the object is created and then the constructor method on it is called.
If you call a function with .call() or .apply() as in function.call(xxx), then you can determine exactly what this is set to by what argument you pass to .call() or .apply(). You can read more about .call() here and .apply() here on MDN.
If you use function.bind(xxx) this creates a small stub function that makes sure your function is called with the desired value of this. Internally, this likely just uses .apply(), but it's a shortcut for when you want a single callback function that will have the right value of this when it's called (when you aren't the direct caller of the function).
In a callback function, the caller of the callback function is responsible for determining the desired value of this. For example, in an event handler callback function, the browser generally sets this to be the DOM object that is handling the event.
There's a nice summary of these various methods here on MDN.
So, in your case, you are making a normal function call when you call privateFunction(). So, as expected the value of this is set as in option 2 above.
If you want to explictly set it to the current value of this in your method, then you can do so like this:
var Example = (function() {
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction.call(this);
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Other methods such as using a closure and defined var that = this are best used for the case of callback functions when you are not the caller of the function and thus can't use 1-4. There is no reason to do it that way in your particular case. I would say that using .call() is a better practice. Then, your function can actually use this and can behave like a private method which appears to be the behavior you seek.
I guess most used way to get this done is by simply caching (storing) the value of this in a local context variable
function Example() {
var that = this;
// ...
function privateFunction() {
console.log(that);
}
this.publicFunction = function() {
privateFunction();
}
}
a more convenient way is to invoke Function.prototype.bind to bind a context to a function (forever). However, the only restriction here is that this requires a ES5-ready browser and bound functions are slightly slower.
var privateFunction = function() {
console.log(this);
}.bind(this);
I would say the proper way is to use prototyping since it was after all how Javascript was designed. So:
var Example = function(){
this.prop = 'whatever';
}
Example.prototype.fn_1 = function(){
console.log(this.prop);
return this
}
Example.prototype.fn_2 = function(){
this.prop = 'not whatever';
return this
}
var e = new Example();
e.fn_1() //whatever
e.fn_2().fn_1() //not whatever
Here's a fiddle http://jsfiddle.net/BFm2V/
If you're not using EcmaScript5, I'd recommend using Underscore's (or LoDash's) bind function.
In addition to the other answers given here, if you don't have an ES5-ready browser, you can create your own "permanently-bound function" quite simply with code like so:
function boundFn(thisobj, fn) {
return function() {
fn.apply(thisobj, arguments);
};
}
Then use it like this:
var Example = (function() {
function Example() {
var privateFunction = boundFn(this, function() {
// "this" inside here is the same "this" that was passed to boundFn.
console.log(this);
});
this.publicFunction = function() {
privateFunction();
}
}
return Example;
}()); // I prefer this order of parentheses
Voilà -- this is magically the outer context's this instead of the inner one!
You can even get ES5-like functionality if it's missing in your browser like so (this does nothing if you already have it):
if (!Function.prototype.bind) {
Function.prototype.bind = function (thisobj) {
var that = this;
return function() {
that.apply(thisobj, arguments);
};
}:
}
Then use var yourFunction = function() {}.bind(thisobj); exactly the same way.
ES5-like code that is fully compliant (as possible), checking parameter types and so on, can be found at mozilla Function.prototype.bind. There are some differences that could trip you up if you're doing a few different advanced things with functions, so read up on it at the link if you want to go that route.
I would say assigning self to this is a common technique:
function Example() {
var self = this;
function privateFunction() {
console.log(self);
}
self.publicFunction = function() {
privateFunction();
};
}
Using apply (as others have suggested) also works, though it's a bit more complex in my opinion.
It might be beyond the scope of this question, but I would also recommend considering a different approach to JavaScript where you actually don't use the this keyword at all. A former colleague of mine at ThoughtWorks, Pete Hodgson, wrote a really helpful article, Class-less JavaScript, explaining one way to do this.
I have this Javascript constructor-
function TestEngine() {
this.id='Foo';
}
TestEngine.prototype.fooBar = function() {
this.id='bar';
return true;
}
TestEngine.prototype.start = function() {
this.fooBar();
}
TestEngine.prototype.startMethod = function() {
inter = setInterval(this.start, 200);
}
var test = new TestEngine();
test.startMethod();
Gives me this error -
Uncaught TypeError: Object [object global] has no method 'fooBar'
I tried console.log and found out that when I call this.start from within setInterval, this points to the window object. Why is this so?
The this pointer can point to one of many things depending upon the context:
In constructor functions (function calls preceded by new) this points to the newly created instance of the constructor.
When a function is called as a method of an object (e.g. obj.funct()) then the this pointer inside the function points to the object.
You can explicitly set what this points to by using call, apply or bind.
If none of the above then the this pointer points to the global object by default. In browsers this is the window object.
In your case you're calling this.start inside setInterval. Now consider this dummy implementation of setInterval:
function setInterval(funct, delay) {
// native code
}
It's important to understand that start is not being called as this.start. It's being called as funct. It's like doing something like this:
var funct = this.start;
funct();
Now both these functions would normally execute the same, but there's one tiny problem - the this pointer points to the global object in the second case while it points to the current this in the first.
An important distinction to make is that we're talking about the this pointer inside start. Consider:
this.start(); // this inside start points to this
var funct = this.start;
funct(); // this inside funct (start) point to window
This is not a bug. This is the way JavaScript works. When you call a function as a method of an object (see my second point above) the this pointer inside the function points to that object.
In the second case since funct is not being called as a method of an object the fourth rule is applied by default. Hence this points to window.
You can solve this problem by binding start to the current this pointer and then passing it to setInterval as follows:
setInterval(this.start.bind(this), 200);
That's it. Hope this explanation helped you understand a little bit more about the awesomeness of JavaScript.
Here is a neat way to do OOP with javascript:
//Global Namespace:
var MyNamespace = MyNamespace || {};
//Classes:
MyNamespace.MyObject = function () {
this.PublicVar = 'public'; //Public variable
var _privatVar = 'private'; //Private variable
//Public methods:
this.PublicMethod = function () {
}
//Private methods:
function PrivateMethod() {
}
}
//USAGE EXAMPLE:
var myObj = new MyNamespace.MyObject();
myObj.PublicMethod();
This way you encapsulate your methods and variables into a namespace/class to make it much easier use and maintain.
Therefore you could write your code like this:
var MyNamespace = MyNamespace || {};
//Class: TestEngine
MyNamespace.TestEngine = function () {
this.ID = null;
var _inter = null;
//Public methods:
this.StartMethod = function (id) {
this.ID = id;
_inter = setInterval(Start, 1000);
}
//Private methods:
function Start() {
FooBar();
console.log(this.ID);
}
function FooBar() {
this.ID = 'bar';
return true;
}
}
//USAGE EXAMPLE:
var testEngine = new MyNamespace.TestEngine();
testEngine.StartMethod('Foo');
console.log(testEngine.ID);
Initially, the ID is set to 'Foo'
After 1 second the ID is set to 'bar'
Notice all variables and methods are encapsulated inside the TestEngine class.
Try this:
function TestEngine() {
this.id='Foo';
}
TestEngine.prototype.fooBar = function() {
this.id='bar';
return true;
}
TestEngine.prototype.start = function() {
this.fooBar();
}
TestEngine.prototype.startMethod = function() {
var self = this;
var inter = setInterval(function() {
self.start();
}, 200);
}
var test = new TestEngine();
test.startMethod();
setInterval calls start function with window context. It means when start gets executed, this inside start function points to window object. And window object don't have any method called fooBar & you get the error.
Anonymous function approach:
It is a good practice to pass anonymous function to setInterval and call your function from it. This will be useful if your function makes use of this.
What I did is, created a temp variable self & assigned this to it when it is pointing your TestEngine instance & calling self.start() function with it.
Now inside start function, this will be pointing to your testInstance & everything will work as expected.
Bind approach:
Bind will make your life easier & also increase readability of your code.
TestEngine.prototype.startMethod = function() {
setInterval(this.start.bind(this), 200);
}
I would like to do something like this:
function end(){ console.log(this); } // <-- the problem is here with `this`
eval('var a = 0; setTimeout(function(){ a = 10; end(); }, 2000)');
which 2 seconds later should output:
{ "a" : 10 }
Is this somehow possible?
Yes:
function end(){ console.log(this); }
eval('var a = 0, self = this; setTimeout(function(){ a = 10; end.call(self); }, 2000)');
Note that I set a variable, self, to be this, and then use Function#call when calling end, which allows us to set a specific value for this during the call. This works because the anonymous function passed to setTimeout has a reference to the execution context in which it was created and all variables within that, and so has access to self (and a).
If there's not a really good reason for using eval (and I don't see one here), I wouldn't, just do this:
function end(){ console.log(this); }
var a = 0, self = this; setTimeout(function(){ a = 10; end.call(self); }, 2000);
You can also create a second function that, when called, turns around and calls end with the right this value. This is called binding, and is facilitated by the ES5 Function#bind function:
function end(){ console.log(this); }
var a = 0, boundEnd = end.bind(this); setTimeout(function(){ a = 10; boundEnd(); }, 2000);
Since you're using NodeJS, you're using V8, which has Function#bind. (If you were doing this in a browser, you'd have to be careful to provide a shim for bind if you needed to support older browsers.)
I had a "class" defined and was making only one instance of it. The instance possessed a member function that would end up being passed around (it's a mouse handler, but that's not important). Since I would only ever make one instance of my "class", I decided to rewrite it as a singleton by using an object literal.
So I have
var mySingleton = {
theObjects : [];
}
mySingleton.mouseHandler = (function() {
var that = this;
return function (e) {
for (var indx = 0; indx < that.theObjects.length; indx++) {
// do something to that.theObjects[indx];
}
}
}());
mySingleton.addObject = function(newObj) {
this.theObjects.push(newObj);
}
However, when I try to use this code (after adding a few objects), I keep getting an that.theObjects is undefined error. It's referring to the line in the for loop.
Update for 2015 – Use Function.bind() to specify the value of this within the function. Then, instead of using that, you can use this.
mySingleton.mouseHandler = function (e) {
for (var indx = 0; indx < this.theObjects.length; indx++) {
// do something to this.theObjects[indx];
}
}.bind(mySingleton);
This doesn't work if you want mouseHandler to have the context of the 'moused' element. For that, use my original answer below.
If you need to support IE8 or (heaven forbid) earlier, you'll need to use a polyfill.
Since you are calling the function that creates mouseHandler immediately, it is run in the context of window, not mySingleton. So that refers to window. Instead of calling it immediately, just change it to a method so that it runs in the context of mySingleton:
mySingleton.getMouseHandler = function() {
var that = this;
return function() { ... };
};
myElement.onclick = mySingleton.getMouseHandler();
Of course, since you are already using a singleton, you can just reference it directly. In your click handler, instead of checking that.theObjects, check mySingleton.theObjects. Or, in mouseHandler change var that = this to var that = mySingleton.
Edit: Or, pass the context to your anonymous function when you call it:
mySingleton.mouseHandler = (function() {
var that = this;
return function (e) {
for (var indx = 0; indx < that.theObjects.length; indx++) {
// do something to that.theObjects[indx];
}
}
}).call(mySingleton);
There are a few popular ways to do this. First, super-simple solution is just reference mySingleton directly and bypass the confusion associated with this. Instead of that.theObjects just do mySingleton.theObjects and move on with your life and things will work fine.
However, there is a common pattern to do this binding. Here's how underscore.js does it
Check out the annoted source to underscore, where you will find this
_.bind = function(func, obj) {
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
var args = slice.call(arguments, 2);
return function() {
return func.apply(obj, args.concat(slice.call(arguments)));
};
};
The other answers here so far are also correct. Providing my viewpoint here in case it helps.
The key to understanding why the code doesn't behave as you expect requires understanding how this works in JavaScript. The problem is that this depends on how the function is called.
First, if you call the function in the method style, this is what you'd expect:
mySingleton.mouseHandler(); // this === mySingleton
If you attach the function to something esle, that works too.
var anotherSingleton = {};
anotherSingleton.foo = mySingleton.mouseHandler;
anotherSingleton.foo(); // this === anotherSingleton
If you detach the function, this becomes the global scope object (window)
var foo = mySingleton.mouseHandler;
foo(); // this === window
And finally, you can force this to be something else using call or apply:
var randomThingy = {};
mySingleton.mouseHandler.call(randomThingy); // this === randomThingy
The takeaway is that this is determined at runtime based on the context of how the function was called. Often, frameworks that allow you to make "classes" abstract these details from you by implicitly applying the bind pattern on your behalf. This is why it used to work, and no longer does.
As others have mentioned, you can change your handler to reference the variable by its scoped name (mySingleton) or otherwise bind it as discussed.
Here's an article I wrote on the subject a few years ago that goes into more detail: http://trephine.org/t/index.php?title=Understanding_JavaScript%27s_this_keyword
Hope this helps!