What is "this" when in the callback of setTimeout? [duplicate] - javascript

This question already has answers here:
Pass correct "this" context to setTimeout callback?
(6 answers)
Closed 5 years ago.
Apologies for the newbie question, but consider this code
function Greeter( name) { this.name = name; }
Greeter.prototype.delayed_greet = function() {
setTimeout( function cb() {
console.log(' Hello ' + this.name);
}, 500);
};
Greeter.prototype.greet = function() {
console.log(' Hello ' + this.name);
}
const greeter = new Greeter(' World');
greeter.delayed_greet(); // will print "Hello undefined"
greeter.greet(); // will print "Hello World"
So in the delayed_greet method, what does this refer to when it's nested inside the setTimeout? It's obviously not referring to the greeter object otherwise it would work.

setTimeout is generally defined as window.setTimeout in browsers, and can be called as just setTimeout because it's available in the global scope.
That also means the context, and this value, is always window, unless another this value is explicitly set.
MDN says
Code executed by setTimeout() is called from an execution context
separate from the function from which setTimeout was called.
The usual rules for setting the this keyword for the called function
apply, and if you have not set this in the call or with bind, it
will default to the global (or window) object in non–strict mode, or
be undefined in strict mode.
It will not be the same as the this value for the function that
called setTimeout.
MDN also outlines a number of ways to solve the "this-problem" in setTimeout.
Personally I think I would just take the easy way out, and use a variable
Greeter.prototype.delayed_greet = function() {
var that = this;
setTimeout( function cb() {
console.log(' Hello ' + that.name);
}, 500);
};
Another option would be an arrow function, as they keep the surrounding context and don't create their own context.
var o = {
fn () {
setTimeout( () => { console.log(this) }, 500)
}
}
var o2 = {
fn () {
setTimeout( function() {
console.log(this === window)
}, 1000)
}
}
o.fn(); // fn() --- arrow function
o2.fn(); // true, is window --- regular function

Related

How do I reference class variables/methods from nested functions? [duplicate]

This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
How to access the correct `this` inside a callback
(13 answers)
Closed 5 years ago.
class Foo {
constructor() {
this.foobar = "foobar";
}
bar() {
let _this = this;
return function() {
try {
alert("Attempt 1: "+foobar);//ReferenceError: foobar is not defined
myMethod();
} catch(err) {console.log(err);}
try {
alert("Attempt 2: "+this.foobar);//TypeError: this is undefined
this.myMethod();
} catch(err) {console.log(err);}
try{
alert("Attempt 3: "+_this.foobar);//Works!
_this.myMethod();
} catch(err) {console.log(err);}
}();
}
myMethod() {
alert("myMethod()");
}
}
new Foo().bar();
The above example is very simplified - the anonymous function inside bar() was a jQuery call originally, but for the sake of the question I didn't include that.
Why don't attempts 1 and 2 work? Do I have to use the _this trick to reference class variables/methods? How do I reference class variables/methods from nested functions?
Are you familiar with how the this keyword works in JavaScript? It's value will depend on how the function is called, not in how it is defined. For example, if you do the following:
var dog = {
greeting:"woof",
talk:function (){
console.log(this.greeting);
}
};
var cat={
greeting:"meow",
talk:dog.talk
};
dog.talk();
cat.talk();
You will see that when the talk function is called as a method of an object, that object will be used as the value of this.
The same happens with ES6 classes, where class methods are still JavaScript functions and the rules for deciding the value of this still apply. If you want to avoid declaring an auxiliar variable, you should look into using bind:
var mammal = {
greeting:"<noise>",
getTalk:function (){
return function(){
console.log(this.greeting);
};
},
getTalkBinded:function (){
return (function(){
console.log(this.greeting)
}).bind(this);
}
};
var dog={
greeting:"woof",
talk:mammal.getTalk(),
talkBinded:mammal.getTalkBinded()
};
var cat={
greeting:"meow",
talk:mammal.getTalk(),
talkBinded:mammal.getTalkBinded()
};
dog.talk();
cat.talk();
dog.talkBinded();
cat.talkBinded();
You are returning self-execution function execution result and during that function execution this it global context(not your class object). to make it work use () => {}() arrow function call syntax, as it captures current context, or function() { }.bind(this)().
See this simple example,
function a(){
this.someProp = 5;
console.log(this);
var _this = this; //so we explicitly store the value of `this` to use in a nested function
return function(){
//value of `this` will change inside this function
this.anotherProp = 6;
console.log(this);
//to use the methods and props of original function use `_this`
console.log(_this)
}
}
var c = a.call({}) //prints {someProp: 5}
c.call({}) //prints {anotherProps: 6} {someProp: 5}

this context lost when passing a reference to a method [duplicate]

This question already has answers here:
Pass correct "this" context to setTimeout callback?
(6 answers)
Closed 6 years ago.
In this example
class Car {
describe() {
console.log("I have " + this.wheels);
}
testMe() {
setTimeout( this.describe, 1000);
}
constructor(wheels) {
this.wheels = wheels;
}
}
let myCar = new Car(4);
myCar.testMe(); // I have undefined
How come the expected value of this isn't passed inside the describe function ?
edit : Can you confirm that is setTimeout was an arrow function, I wouldn't get undefined ?
A bare function reference has no context, even if it was retrieved as a property of an object.
Within your testMe function you need either:
setTimeout(() => this.describe(), 1000);
or
setTimeout(this.describe.bind(this), 1000);
or
let self = this;
setTimeout(function() {
self.describe();
}, 1000);
That last one could be written as an arrow function, of course, but if you're doing that you might as well use the first version, with no local copy of this required.
Regarding your edit, it is possible to pass just this.describe by making describe an arrow function, assigned from inside the constructor, but note that this then means that every instance of the object would have its own copy of that function instead of there just being a single copy on the prototype of the class:
constructor(wheels) {
this.wheels = wheels;
this.describe = () => {
console.log("I have " + this.wheels);
}
}

difference between (callback) and (() => callback()) in typescript (angular2) [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 1 year ago.
Normally I'd assign an alternative "self" reference when referring to "this" within setInterval. Is it possible to accomplish something similar within the context of a prototype method? The following code errors.
function Foo() {}
Foo.prototype = {
bar: function () {
this.baz();
},
baz: function () {
this.draw();
requestAnimFrame(this.baz);
}
};
Unlike in a language like Python, a Javascript method forgets it is a method after you extract it and pass it somewhere else. You can either
Wrap the method call inside an anonymous function
This way, accessing the baz property and calling it happen at the same time, which is necessary for the this to be set correctly inside the method call.
You will need to save the this from the outer function in a helper variable, since the inner function will refer to a different this object.
var that = this;
setInterval(function(){
return that.baz();
}, 1000);
Wrap the method call inside a fat arrow function
In Javascript implementations that implement the arrow functions feature, it is possible to write the above solution in a more concise manner by using the fat arrow syntax:
setInterval( () => this.baz(), 1000 );
Fat arrow anonymous functions preserve the this from the surrounding function so there is no need to use the var that = this trick. To see if you can use this feature, consult a compatibility table like this one.
Use a binding function
A final alternative is to use a function such as Function.prototype.bind or an equivalent from your favorite Javascript library.
setInterval( this.baz.bind(this), 1000 );
//dojo toolkit example:
setInterval( dojo.hitch(this, 'baz'), 100);
i made a proxy class :)
function callback_proxy(obj, obj_method_name)
{
instance_id = callback_proxy.instance_id++;
callback_proxy.instances[instance_id] = obj;
return eval('fn = function() { callback_proxy.instances['+instance_id+'].'+obj_method_name+'(); }');
}
callback_proxy.instance_id = 0;
callback_proxy.instances = new Array();
function Timer(left_time)
{
this.left_time = left_time; //second
this.timer_id;
this.update = function()
{
this.left_time -= 1;
if( this.left_time<=0 )
{
alert('fin!');
clearInterval(this.timer_id);
return;
}
}
this.timer_id = setInterval(callback_proxy(this, 'update'), 1000);
}
new Timer(10);

Settimeout, bind and this

Here I have copied code snippet from MDN : https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
function LateBloomer() {
this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.declare = function() {
console.log('I am a beautiful flower with ' +
this.petalCount + ' petals!');
};
var flower = new LateBloomer();
flower.bloom();
// after 1 second, triggers the 'declare' method
The most confusing part is : window.setTimeout(this.declare.bind(this), 1000);
I understand how this works and this inside settimeout is always bound to global object.I know there can be var self or var that inside bloom function.
There are two this in that line but which this refers to what and how that works is totally confusing.
How works?
First of all read this article, which offers a very nice explanation about how this works.
.bind(this, args) just helps you to pass your this context inside your function (because inside it in your example by default this is undefined or refers to window).
Also bind is a nice alternative to this:
// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
var self = this;
window.setTimeout(self.declare, 1000);
};
And as the last point in es6 you can do it in this way:
window.setTimeout(() => {
//do some stuff
}, 1000);
instead of
window.setTimeout(function () {
//do some stuff
}.bind(this), 1000);
this allow you to not think about this.
MSDN defines Function.prototype.bind() as,
The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of
arguments preceding any provided when the new function is called.
By using .bind(this) we are passing this to the function declare
See this snippet.
function LateBloomer() {
console.log(this.constructor);
this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.undefinedbloom = function() {
window.setTimeout(this.declare, 1000);
};
LateBloomer.prototype.declare = function() {
console.log(this.constructor);
console.log('I am a beautiful flower with ' +
this.petalCount + ' petals!');
};
var flower = new LateBloomer();
flower.bloom();
flower.undefinedbloom();
In the function undefinedbloom we are just calling the declare function. So the object will be the window object. It doesn't have property petalCount so its undefined.
In the function bloom we are binding the this of LateBloomer to the declare function by which we will have access to the LateBloomer's object petalCount.
this in JavaScript is a very difficult to fathom at first.
MDN Link: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/this
function LateBloomer() {
this.name = 'triven'; //simplified property
}
// Some thoughts we discuss main requirement.
LateBloomer.prototype.bloom = function() {
window.setTimeout(function() {console.log(this);}, 1000); //Logs window: We all know that keyword this INSIDE CALLBACK
//function refers to Window [Comment 1]
//window.setTimeout(console.log(this), 1000); /Attentions: Here it is very easy to MISUNDERSTAND
//that keyword this is inside setTimeout and it should refer to window.
//Please note that here keyword this is not INSIDE CALLBACK function so here
//keyword this will refer to object on which its wrapper function is
//executed(in our case flower). [Comment 2]
};
//The requirement: We need to call .bloom and it should log name after 1 second.
LateBloomer.prototype.bloom = function() {
//here keyword this refers to object
//window.setTimeout(function() {console.log(this);}, 1000); //But inside callback keyword this refers to window.
};
//We need some way to access object inside call back function so that its name can be accessed after 1 sec.
//step 1; Taking out anonymous function and adding it as a prototype property
LateBloomer.prototype.bloom = function() {
//window.setTimeout(this.callback, 1000); //Note: Keyword this is not inside callback so
//it is referring to object (not window). We can access newly
//defined function on the object. Also keyword this placed inside callback
//function(below) will still refer to window.
};
LateBloomer.prototype.callback = function() {console.log(this.name);}
//step 2; bringing .bind into picture.
//Definition of .bind as per MDN : The bind() method creates a new function
//that, when called, has its this keyword set to the provided value
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.callback.bind(this), 1000); // Okay now we are now invoking .callback method on the object and
//passing the same object to bind.
// The keyword this inside newly return function (as a result of bind) will now refer to object
// we passed as argument to bind and we should not be able to access name property of our object.
// Note : Here both this keywords refers to same object ie on which which are calling .bloom.
//Note : we are no longer using passing callback function defined below. Instead we are now passing
// and exact copy of it but configuring it in such a way that keyword this start refering to object we passed.
};
LateBloomer.prototype.callback = function() {console.log(this.name);}

JavaScript setTimeout, using call to change this

I have two simple links that when clicked should wait one second and then add a class that changes the color of the text. The working version uses $.proxy and the non-working version I'm trying to use native JavaScript to change the meaning of this. Why is btnWaitNoProxy this still referring to the global object?
fiddle
code:
var obj = {
wait: function () {
setTimeout(function () {
console.log('inside the setTimeout');
$(this).addClass('lesson');
//refers to global object in the console
}, 1000);
}
};
$('#btnProxy').on('click', function () {
console.log('preparing to add class...');
setTimeout($.proxy(function () {
$(this).addClass('lesson')
console.log(this);
}, this), 1000);
});
$('#btnWaitNoProxy').on('click', function () {
console.log(this);
//call still refers to the global object
obj.wait.call(this);
});
Because you are using setTimeout in wait, so that callback method passed to setTimeout will be executed in the global context by default.
One possible solution is to use $.proxy() as you have done again with the setTimeout handler
var obj = {
wait: function () {
setTimeout($.proxy(function () {
console.log('inside the setTimeout');
$(this).addClass('lesson');
//refers to global object in the console
}, this), 1000);
}
};
Another is to use a closure variable like
var obj = {
wait: function () {
var self = this;
setTimeout(function () {
console.log('inside the setTimeout');
$(self).addClass('lesson');
//refers to global object in the console
}, 1000);
}
};
When you pass a method to setTimeout() (or any other function, for that matter), it will be invoked with a wrong this value.
Code executed by setTimeout() is run in a separate execution context to the function from which it was called. As a consequence, the this keyword for the called function will be set to the window (or global) object; it will not be the same as the this value for the function that called setTimeout.
Source: MDN — window.setTimeout
There are plenty solutions:
Using $.proxy
Using Function.prototype.bind
Aliasing this

Categories