I am trying to use setTimeout() inside a class function in JavaScript. The setTimeout() is supposed to trigger another method in the same Class, so the function I am passing it is written as window.setTimeout("this.anotherMethod", 4000). That bring the problem: this references the calling Object, in the case of setTimeout() it is window. How can I use enclosures to return a reference to the Class Object itself?
myObject = function(){
this.move = function(){
alert(this + " is running");
}
this.turn = function(){
alert(this + " is turning");
}
this.wait = function(){
window.setTimeout("this.run" ,(1000 * randomNumber(1,5)));
}
this.run = function(){
switch(randomNumber(0,2)){
case 0:
this.move();
break;
case 1:
this.turn();
break;
case 2:
this.wait();
}
}
}
You can do this:
var that = this;
setTimeout(function () {
that.doStuff();
}, 4000);
You can also bind for more succinct code (as originally pointed out by #Raynos):
setTimeout(this.doStuff.bind(this), 4000);
bind is a standard library function for exactly this coding pattern (ie capturing this lexically).
You can also bind a function to scope.
setTimeout(this.run.bind(this) ,(1000 * randomNumber(1,5)));
Be warned Function.prototype.bind is ES5
this can be problematic in javascript, as you've discovered.
I usually work around this by aliasing this inside the object so that I can use the alias whenever I need a reference back to the containing object.
MyObject = function ()
{
var self = this;
// The rest of the code goes here
self.wait = function(){
window.setTimeout(self.run ,(1000 * randomNumber(1,5)));
}
}
this.wait = function(){
var self = this;
window.setTimeout(function() { self.run() } ,(1000 * randomNumber(1,5)));
}
So you store the reference to the object you're calling .run on in a local variable ('self').
class A{
setTimeout(()=>{
// here this != undefined because of arrow function
},500);
}
this is sensitive to the context in which it is called. When you pass a string to setTimeout then that is evaled in a completely different context.
You need to preserve the current value of this (by copying it to a different variable) and maintain the scope (by not using (implied) eval).
this.wait = function(){
var self = this;
setTimeout(function () { self.run() },
(1000 * randomNumber(1,5))
);
}
At the top of your main myObject make a new reference to the current value of this:
var self = this;
and then create a closure for your timer callback that uses that new reference instead of the global object that setTimeout will use as the default context in callbacks:
setTimeout(function() {
self.run();
}, 4000);
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
inside func, this always refer to the global object. you can pass in the current object into func,
var timeoutID = window.setTimeout(func, delay, this);
function func(that) {...}
unfortunately it does NOT work in IE
Note that passing additional parameters to the function in the first syntax does not work in Internet Explorer.
you can just use the arrow function syntax:
setTimeout(() => {
this.doStuff();
}, 4000);
Have you tried;
window.setTimeout("myObject.run" ,(1000 * randomNumber(1,5)));
You can use this code instead, which works in all modern browsers -
setTimeout(function(thisObj) {thisObj.run();},1000,this);
Ref: http://klevo.sk/javascript/javascripts-settimeout-and-how-to-use-it-with-your-methods/
Shorter way. Without anonymous func.
var self = this;
setTimeout(self.method, 1000);
It is not recommended to use setTimeout or setInterval using strings
setTimeout("myFunction()", 5000);
//this is the same as
setTimeout(function(){ eval("myFunction()"); }, 5000)); //<-- eval == BAD
Ran into a more complex situation...class A has a member of type B and a method that calls setTimeout which calls a method on class B. Solved as follows:
class A {
constructor(b) {
this.b = b;
}
setTimer(interval) {
setTimeout(this.b.tick.bind(this.b), interval);
}
}
class B {
constructor(name){
this.name = name;
this.ele = window.document.getElementById('B');
}
tick() {
console.log(this);
this.ele.innerText += ' ' + this.name;
}
}
Which bound A.b to this within B.tick and worked.
Here's a fiddle with bind: https://jsfiddle.net/jrme9hyh/
And one without bind which fails: https://jsfiddle.net/2jde8tq3/
Related
So I can't quite figure out why the variable this.tasks becomes undefined inside of the add event listener I have inside of my goal object. I have a feeling it might have something to do with asynchronous programming(which I still don't fully understand). Sorry I'm a bit of a JS noob, but if you guys could explain to me what I'm doing wrong and what might be a better solution that would be awesome! Thanks.
function Goal(name) {
this.gDiv = document.createElement('div');
this.name = name || "goal";
this.tasks = document.createElement('ul');
//Sets the styling and content and adds it to the parent element
this.initialize = function() {
this.gDiv.className = "default";
this.gDiv.setAttribute("id", this.name);
this.gDiv.innerHTML = this.name;
elem.appendChild(this.gDiv);
this.gDiv.parentNode.insertBefore(this.tasks, this.gDiv.nextSibling);
this.tasks.style.display = "none";
};
//Creates a list underneath the a dive associated with the Goal object
this.addTask = function(task) {
var newLi = document.createElement('li');
newLi.innerHTML = task;
this.tasks.appendChild(newLi);
};
this.gDiv.addEventListener('click', function(){
alert(this.tasks);
});
}
Thank you guys! You all answered my question! I'd been scratching my head at this for a while. Kudos to you all!
The scope changes when you enter that anonymous closure and 'this' changes. You can hack around it by doing
var self = this;
And then using self in place of this (eg):
function Goal(name) {
var self = this;
/* ... */
this.gDiv.addEventListener('click', function(){
alert(self.tasks);
});
If you're using jQuery you could do something nicer:
this.gDiv.addEventListener('click', $.proxy(function() {
alert(this.tasks);
}, this));
Either way works just fine.
EDIT: In ES6, arrow functions can be used instead as they don't bind their own "this", so it becomes even simpler:
this.gDiv.addEventListener('click', () => {
alert(this.tasks);
});
Here is a comparison of some methods (including your problem), to give you a taster, and to try and explain things a little.
// This is the problem that you have,
// where `this` inside the anonymous function
// is a different scope to it's parent
function Test1(something) {
// `this` here refers to Test1's scope
this.something = something;
setTimeout(function() {
// `this` here refers to the anonymous function's scope
// `this.something` is `undefined` here
console.log(this.something);
}, 1000);
};
new Test1('Hello');
// This solution captures the parent `this` as `test2This`,
// which can then be used inside the anonymous function
function Test2(something) {
var test2This = this;
this.something = something;
setTimeout(function() {
console.log(test2This.something);
}, 1000);
}
new Test2('World');
// This solution captures `this` as `test3This` in an `IIFE closure`
// which can then be used in the anonymous function
// but is not available outside of the `IIFE closure` scope
function Test3(something) {
this.something = something;
(function(test3This) {
setTimeout(function() {
console.log(test3This.something);
}, 1000);
}(this));
}
new Test3('Goodbye');
// This method requires that you load an external library: jQuery
// and then use it's `$.proxy` method to achieve the basics of
// Test3 but instead of being referred to as `test3This` the
// outer scope `this` becomes the inner scope `this`
// Ahh, that's much clearer?
function Test4(something) {
this.something = something;
setTimeout($.proxy(function() {
console.log(this.something);
}, this), 1000);
}
new Test4('Mum');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
// This is approximately what jQuery's `$.proxy` does
// but without having to load the whole library
function Test5(something) {
this.something = something;
setTimeout((function(func, context) {
return function() {
func.call(context);
};
}(function() {
console.log(this.something);
}, this)), 1000);
}
new Test5('Dad');
// Lets create the proxy method as a reuseable
function proxy(func, context) {
var args = Array.prototype.slice.call(arguments, 2);
return function() {
return func.apply(
context,
args.concat(Array.prototype.slice.call(arguments))
);
};
}
// and now using it
function Test6(something) {
this.something = something;
setTimeout(proxy(function() {
console.log(this.something);
}, this), 1000);
}
new Test6('Me want cookies');
Then we have Function#bind
function Test7(something) {
this.something = something;
setTimeout(function() {
// `this` was bound to the parent's `this` using bind
console.log(this.something);
}.bind(this), 1000);
};
new Test7('Num num');
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.min.js"></script>
And most recently ES2015 Arrow functions
function Test8(something) {
this.something = something;
setTimeout(() => console.log(this.something), 1000);
};
new Test8('Whoop');
In ES6, arrow functions were introduced, which do not bind their own this.
MDN for reference.
So creating an anonymous function using the arrow syntax is probably the easiest way to overcome this issue nowadays. It is supported by all major browsers currently, except IE.
the keyword 'this' changes in it's meaning for an event handler against a constructor
please refer to the MDN
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#As_a_DOM_event_handler
Basically I want to do this:
someFunction() // do something
someFunction.somePropertyFunction()
someFunction() // Now someFunction is modified; it should now exhibit a different behaviour
Is this possible?
EDIT:
I'm not looking for what #Kolink was suggesting. Basically I want to augment a function's functionality by calling one of it's property function.
Specifically, I need to: 1. have access to the original function inside my property function (which is entirely doable using this), and 2. bind a new function to the original function's name (which I'm not sure if it's possible).
Just to be clear, I don't have access to the internal definition of the function that I want to augment. I want to attach a function to Function.prototype (so that it will be available as a property of the function that I want to augment), and then I will call func.augmentThis(), and then func should be augmented. But I'm not sure how, hence the question :P
Easily. Here's an example:
var derp = 123;
someFunction = function() {alert(derp);};
someFunction.somePropertyFunction = function() {derp = 456;};
someFunction(); // alerts 123
someFunction.somePropertyFunction();
someFunction(); // alerts 456
Okay, that's an oversimplified example, but yeah, it's entirely possible.
If your question is whether a function attached as a property to another function has a way to access the function to which it is attached, the answer is no. After all, the same function could be attached to any number of functions of objects.
So one alternative is to explicitly refer to the "mother" function within the function that is attached to it and intended to change its behavior:
function f (n) { alert (n + f.offset); }
f.offset = 0;
f.change_offset = function (i) { f.offset = i; };
f (1); //1
f.change_offset (100);
f (1); //101
Here, f is hard-wired into the definition of change_offset. If this bothers you, or you want something slightly more general, write a little routine to set a function as a property on another function, while binding its this to the function being attached to:
function set_func_as_func_prop ( propname, func_to_set, func_to_set_on ) {
func_to_set_on[propname] = func_to_set.bind(func_to_set_on);
}
Now you can write the function more generally
function change_offset (i) {
this.offset = i;
}
and set it on f or any other function.
set_func_as_func_prop ("change_offset", change_offset, f);
set_func_as_func_prop ("change_offset", change_offset, g);
Sort of:
function someFunction() {
return realFunction.apply(this, arguments);
}
function someFunctionA(name) {
return 'Hello, ' + name + '!';
}
function someFunctionB(name) {
return 'Goodbye, ' + name + '...';
}
var realFunction = someFunctionA;
someFunction.somePropertyFunction = function () {
realFunction = someFunctionB;
};
Sure it's possible. It's not recommended, but it's possible. For example:
function a() {
alert("a");
}
function b() {
alert("b");
}
function c() {
return c.f.apply(this, arguments);
}
c.f = a;
c.toggle = function () {
c.f = c.f === a ? b : a;
};
Now let's test it:
c(); // alerts "a"
c.toggle();
c(); // alerts "b"
See the demo: http://jsfiddle.net/LwKM3/
I want to attach a function to Function.prototype. Then I need to bind a new function to the original function's name (which I'm not sure if it's possible).
That indeed is impossible, you don't know what refers to the function. And you cannot change the internal representation of a function, which is immutable.
The only thing you can do is to create a new function and return that, to let the caller of your method use it somehow - specifically assigning it to the original variable:
somefunction = somefunction.augmentSomehow();
Your method for that will look like this:
Function.prototype.augmentSomehow = function() {
var origFn = this;
return function() {
// in here, do something special
// which might include invoking origFn() in a different way
};
};
Not sure if this helps, but I would implement described problem in following way:
// defined by somebody else - unknown to developer
var someFunction = function() {
alert("this is initial behavior");
}
someFunction(); // returns "this is initial behavior"
// defines parent object on which someFunction() is called
var parentObject = this; // returns window object (as called direclty in the
// browser)
// if you are calling someFunction from some object (object.someFunction())
// it would be:
// var parentObject = object;
// augumentThis definition
someFunction.augumentThis = function() {
var newFunction = function() {
alert("this is changed behavior");
};
parentObject.someFunction.somePropertyFunction = function() {
parentObject.someFunction = newFunction;
parentObject.someFunction();
};
};
someFunction.augumentThis(); // change function behavior
someFunction(); // "this is initial behavior"
someFunction.somePropertyFunction(); // "this is changed behavior"
someFunction(); // "this is changed behavior"
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 am using John Resig's Simple JavaScript Inheritance and have run into an issue where I am losing what 'this' refers to. Using this code:
var Runner = Class.extend({
init: function() {
this.update();
if(!this.interval) {
this.interval = setInterval(this.update, this.period * 1000);
}
},
stop: function() {
clearInterval(this.interval);
},
update: function() {
this.success()
},
success: function(){
}
});
var SubRunner = Runner.extend({
update: function() {
this._super();
},
success: function(){
alert('sub runner success');
}
});
Running p = new SubRunner() works as I would expect and alerts sub runner success the first time. After the first run through it then tries to run the success function on the wrong 'this' (window).
I know Prototype gives you a bind function so that you can pass the context to the function but I haven't had any luck in doing something similar here. Does anyone have a starting point to figuring this out?
Thanks!
The problem is when you pass this.update to the setInterval function. In Javascript, the "this" depends on wether you call the function using dot notation, and functions will not remember where they came from if you pass them as callbacks or store them in a variable.
You can either add a wrapper function
var that = this;
setTimeout(function(){ that.update() }, this.perios*1000)
or you can use the bind method if its available in your browser (or you can use the similar function in Prototype).
setTimeout(this.update.bind(this), this.period*1000)
When you pass this.update to setInterval you lose the context.
The simplest solution is to do
var that = this;
this.interval = setInterval(function() { that.update() }, this.period * 1000);
this.interval = setInterval(this.update, this.period * 1000);
When setTimeout calls a function it calls it in the global scope (it sets this to window).
You need to pass a function that calls this.update.
var self = this;
this.interval = setInterval(function(){
self.update();
}, this.period * 1000);
I want to pass an object array to the setTimer function in Javascript.
setTimer("foo(object_array)",1000);
am getting error on this code.
**Note:**Sorry ! some correction in my question : Is it possible in setInterval() function.
Use an anonymous function instead of a string on the first parameter of the setTimeout or setInterval functions:
// assuming that object_array is available on this scope
setInterval(function () { foo(object_array); }, 1000);
Why it works:
When you define an inner function, it can refer to the variables present in their
outer enclosing function even after their parent functions have already terminated.
This language feature is called closures.
If you pass a string as the first argument of these functions, the code will be executed internally using a call to the eval function, and doing this is not considered as a good practice.
Eval provides direct access to the JavaScript compiler and executes the code it's passed with the privileges of the caller, also using eval repeatedly/extensively (i.e. your setInterval function is a good example) will lead to performance issues.
I'm going to expand on Luke's answer here because it addresses a use case that CMS's (and most answers to this kind of question) does not.
If you need to bind your arguments to the function call at the time you set the timeout, a simple function enclosure will not work:
echo = function (txt) { console.log(txt); };
val = "immediate A";
echo(val);
val = "delayed";
window.setTimeout(function () { echo(val); }, 1000);
val = "immediate B";
echo(val);
Assuming you are using Firebug's console, the above will output "immediate A", "immediate B" and then "immediate B" 1 second later. To bind the value at the time of the setTimeout call use Luke's trap method. The following modifies it a little to accept arbitrary functions and argument lengths:
echo = function (txt) { console.log(txt); };
trap = function (fn, args) {
return function() {
return fn.apply(this, args);
};
};
val = "immediate A";
echo(val);
val = "delayed";
window.setTimeout( trap(echo, [val]), 1000);
val = "immediate B";
echo(val);
Not sure if there is a way to pass the caller's context in implicitly, but it could be further expanded to accept a context argument if "this" doesn't get you there.
first, it's 'setTimeout'
second, don't pass a string. The real solution depends on the rest of the code. Most robust way would be to trap the scope:
var obj_array = something;
function trap(obj)
{
function exec() { foo(obj); }
return exec;
}
setTimeout(trap(obj_array), 1000);
trap returns a function that has your array trapped in its scope. This is a generic function but to make it specific to your problem, it can be simplified:
var obj_array = something;
function trap()
{
function exec() { foo(obj_array); }
return exec;
}
setTimeout(trap(), 1000);
or even:
var obj_array = something;
function trap()
{
foo(obj_array);
}
setTimeout(trap, 1000);
and finally condensed down to:
var obj_array = something;
setTimeout(function() { foo(object_array); }, 1000);
EDIT:
My functions (or at least 1 iteration of them I found in a backup here)
Function.prototype.createDelegate = function(inst, args) {
var me = this;
var delegate = function() { me.apply(inst, arguments); }
return args ? delegate.createAutoDelegate.apply(delegate,args) : delegate;
};
Function.prototype.createAutoDelegate = function() {
var args = arguments;
var me = this;
return function() { me.apply({}, args); }
};
GIVEN:
function test(a, b) { alert(a + b); }
USAGE:
setTimeout(test.createAutoDelegate(1, 2), 1000);
OR GIVEN:
var o = { a:1, go : function(b) { alert(b + this.a); }}
USAGE:
setTimeout(o.go.createDelegate(o,[5]), 1000);
//or
setTimeout(o.go.createDelegate(o).createAutoDelegate(5), 1000);