Get object in member function callback - javascript

I have an object with a method that I’d like to pass to a function as a callback. However, inside the callback, this no longer refers to my object. Why not?
I’m familiar with using a variable to get around the problem when passing a function literal:
var obj = {
a: function () {
var me = this;
console.log(this);
setTimeout(function () {
console.log(this); // Not obj
console.log(me); // This works!
}, 100);
}
};
How can I fix it in this case?
var obj = {
b: function () {
setTimeout(this.callback, 100);
},
callback: function () {
console.log(this); // =(
}
};

Yes, this can be kind of tricky in Javascript. The problem is that its value depends on how you call the function.
obj.callback(); //ok
var f = obj.callback;
f(); //does not look like a method call
//Javascript does not pass the this!
The usual workaround is passing a wrapper callback like you did in b), except that the common name for the me variable is that (you sometimes see self too)
var that = this;
setTimeout( function(){ return that.callback(); }, 300);
The other alternative is to use the bind method from functions
setTimeout( this.callback.bind(this) , 300)
Note that bind is not supported in IE 8 (you might need a shim in that case).
For more:
Maintaining the reference to "this" in Javascript when using callbacks and closures

Related

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);

Newly created objects call to constructor undefined if constructor is inside another function

I just learned OOP and there is one little thing that I am not able to solve. The problem is a scope issue of some sort.
If I create a new object then how will I be able to give it access to my constructor function if the constructor function is inside another function? Right now I get undefined. Storing the function in a global variable wont do the job.
var example = new something x(parameter);
example.i();
var getFunction;
var onResize = function() {
getFunction = function something(parameter) {
this.i= (function parameter() {
// Does something
});
};
};
window.addEventListener('resize', onResize);
onResize();
For OOP javascript, the pattern should be like this.
//defining your 'class', class in quotes since everything is just functions, objects, primitives, and the special null values in JS
var Foo = function (options) {
// code in here will be ran when you call 'new', think of this as the constructor.
//private function
var doSomething = function () {
}
//public function
this.doSomethingElse = function () {
}
};
//actual instantiation of your object, the 'new' keyword runs the function and essentially returns 'this' within the function at the end
var foo = new Foo(
{
//options here
}
)
If I understand you, you want to know how to access variables inside another function. Your attempt is reasonable, but note that getFunction is not bound until after onResize is called. Here's a bit of a cleaner demo:
var x;
function y() {
// The value is not bound until y is called.
x = function(z) {
console.log(z);
}
}
y();
x('hello');
A common JavaScript pattern is to return an object that represents the API of a function. For example:
function Y() {
var x = function(z) {
console.log(z);
}
return {
x: x
};
}
var y = Y();
y.x('hello');
You should definitely read up on the basic concepts of JavaScript. Your code and terminology are both sloppy. I'd recommend Secrets of the JavaScript Ninja. It does a good job explaining scope and functions, two tricky topics in JavaScript.

Can I set a local var to 'this' to reference it inside an anonymous callback function?

I would like to reference 'this' in a callback function, but there is no guarantee that 'this' will refer to the correct object. Is it appropriate to create a local variable referencing 'this' and use that variable inside the anonymous function?
Example:
var MyClass = function (property) {
this.property = property;
someAsynchronousFunction(property, function (result) {
this.otherProperty = result; // 'this' could be wrong
});
};
The problem is, the asynchronous function may call the provided callback from an arbitrary context (which is often outside of my control, e.g. when using libraries).
My proposed solution is:
var MyClass = function (property) {
this.property = property;
var myClass = this;
someAsynchronousFunction(property, function (result) {
myClass.otherProperty = result; // references the right 'this'
});
};
But I was looking to see if there are other strategies, or if this solution is problematic in any way.
What you've done is the classical way of making sure you refer to the correct object, though you should define it locally, i.e.:
function(property) {
var that = this;
someFunc(function(result) {
that.property = whatever;
}
}
Alternatively, in modern browsers you can bind it explicitly:
someFunc(function(result) {
this.property = whatever;
}.bind(this));
See also: bind()
Libraries such as jQuery support the latter functionality as a proxy function that more browsers support and can be simplified into this reusable function:
function proxy(fn, ctx)
{
return function() {
return fn.apply(ctx, arguments);
}
}
And to use it:
someFunc(proxy(function(result) {
this.property = whatever;
}, this));
Yes, that's fine, but don't use an implicit global variable as you did, use a local variable:
var myClass = this;

JavaScript variable visibility in handler

I am a little bit confused on how this works, let me post you an example code:
someClass = function() {
this.a = 10;
this.Foo = function() {
console.log(this.a); // will output "10"
setTimeout(this.Bar, 1);
}
this.Bar = function() {
console.log(this.a); // undefined
}
}
var instance = new someClass();
instance.Foo();
My understanding is that this.a is not visible in function Bar if it is called from setTimeout (or some other "handler" type of thing.)
What is the common/correct way of solving that?
(I am trying this in Node.js btw)
Thank you.
The problem is that the scope (this) is lost when you pass a function to setTimeout.
Here's the easiest way to fix it, store this as a reference through a closure and use that instead.
// This uses _me_ everywhere for consistency, but the only place that really needs it
// is within the Bar method. But consistency in this case makes your code more change-
// proof, say if someone calls setTimeout(instance.Foo, 1)
someClass = function() {
var me = this;
me.a = 10;
me.Foo = function() {
console.log(me.a); // will output "10"
// setTimeout will cause me.Bar to be called with window as the context
setTimeout(me.Bar, 1);
}
me.Bar = function() {
// so we avoid using _this_ in here
console.log(me.a); // 10
}
}
A slightly more elegant way is to use Function.bind
someClass = function() {
this.a = 10;
this.Foo = function() {
console.log(this.a); // will output "10"
// the bind call will force it to use the first argument as `this`
setTimeout(this.Bar.bind(this), 1);
}
this.Bar = function() {
console.log(this.a); // undefined
}
}
When passing the function this.Bar as an argument to another function, you need to bind this.Bar to the context you'd like it to be executed with.
If you're using a JS library like jQuery or Underscore.js they already come with that functionality:
setTimeout(_.bind(this.Bar, this), 1);
Here's a simple implementation of a bind function:
var bind = function(scope, fn) {
return function() {
return fn.apply(scope, arguments);
};
}
Update:
As #generalhenry pointed out, node.js already comes with a bind function in (Function.prototype.bind), so you can do this without adding a custom bind function nor an external library:
setTimeout(this.Bar.bind(this), 1);

Am I using Javascript call-backs appropriately (object-oriented style)?

I have the following code example to use an object that receives the action from the callback. Doesn't seem like this is a good design pattern. Or is it?
When setTimeOut() fires on the function after 1 second, it uses the objInstance global variable (DOM scope) to access the ClassExample object instance. Can someone recommend a better way to utilize callbacks within an object oriented design?
The whole idea is so I can use the callback to update data within my object instance (increment a variable for example).
function ClassExample{
this.initiate = function() {
setTimeOut(objInstance.afterTimeOut,1000); //using the objects global handle
}
this.afterTimeOut = function() {
alert("Received!");
}
}
var objInstance = new ClassExample(); //instance
objInstance.initiate();
No, you're not. You'll want to do this:
this.initiate = function() {
setTimeOut(objInstance.afterTimeOut,1000); //using the objects global handle
}
Now, if "afterTimeout" needs the proper object context, you could do this:
this.initiate = function() {
var instance = this;
setTimeout(function() { instance.afterTimeOut(); }, 1000);
}
OK well you changed the question considerably with that little edit :-) If I were you, I'd just do this (like my original second example):
this.initiate = function() {
var instance = this;
setTimeout(function() { instance.afterTimeOut(); }, 1000);
}
Then you don't need any ugly global variables around at all.
edit — Stackoverflow user #Christoph comments that this isn't particularly pretty. One thing that might help would be to use a "bind" facility, as provided by newer browsers natively (as a method on the Function prototype) or by some libraries (Prototype or Functional for example). What "bind" lets you do is create a little wrapper function like I've got above:
this.initiate = function() {
setTimeout(this.afterTimeOut.bind(this), 1000);
}
That call to "bind" returns a function that is effectively the same sort of thing as the little wrapper I coded explicitly in the example.
function ClassExample{
this.afterTimeOut = function() {
alert("Received!");
}; // Don't forget these
setTimeOut(afterTimeOut, 1000); // Don't use () if you're passing the function as an argument
}
var objInstance = new ClassExample(); //instance
That way you don't need the initiate() method.
If you really want the initiate() method, I'd do it like this:
function ClassExample{
var self = this;
self.afterTimeOut = function() {
alert("Received!");
};
self.initiate = function() {
setTimeOut(self.afterTimeOut, 1000);
};
}
var objInstance = new ClassExample(); //instance
objInstance.initiate();
This is how I'd do it to allow timer reuse and minimize the number of closures:
function Timer(timeout, callback) {
this.timeout = timeout;
this.callback = callback;
}
Timer.prototype.run = function(thisArg /*, args... */) {
var argArray = Array.prototype.slice.call(arguments, 1);
var timer = this;
setTimeout(function() {
timer.callback.apply(thisArg, argArray);
}, timer.timeout);
};
var timer = new Timer(1000, alert);
timer.run(null, 'timer fired!');
And just for fun, a golfed version which is functionally equivalent, but replaces the object with a closure:
function delay(func, timeout) {
return function() {
var self = this, args = arguments;
setTimeout(function() { func.apply(self, args); }, timeout);
};
}
delay(alert, 1000).call(null, 'timer fired!');
You are right it is not the optimal way of doing what you are aiming for. however i have to wonder why you need to break the callstack as part of the initiation, it seems very academic.
apart from that if i had to do that, i'd probably use a closure like so:
function ClassExample{
this.initiate = function() {
setTimeOut((function(self) { return function() { self.afterTimeout();}})(this),1000); //using the objects global handle
}
this.afterTimeOut = function() {
alert("Received!");
}
}
var objInstance = new ClassExample(); //instance
objInstance.initiate()
this.initiate = function() {
var instance = this;
setTimeOut(function() {
instance.afterTimeOut();
}, 1000);
};
By saving this to a local variable, you can avoid using the global handle at all. Also this prevent the afterTimeout() from losing it's this.
Building on Znarkus answer...
I really don't know in which environment his code is running but for me the first approach just do not works. I got: 'ReferenceError: afterTimeOut is not defined'...
The second one, nevertheless, is really cool... I just changed setTimeOut for setTimeout (using lowercase 'o') and included parenthesis after the class name definition turning the first line of code into 'function ClassExample(){'; solved my problem.
My snippet of example code:
Oop with private behaviour, intern callback calling and etc.
function MyTry (name){
// keep this object pointer... that's the trick!
var self = this;
// create private variable
var d = new Date()toJSON().slice(0, 10);
// create a private function
function getName(){return name}
// create public access method
self.hello = function(){alert('Hello '+getName()+'!\nToday is: '+d)}
// note instance method hello passed as a callback function!
self.initiate = function(){setTimeout(self.hello, 3000)}
}

Categories