Javascript, preserving this reference when passing function argument - javascript

I'm inside a function of an object and I need to call an external function and pass a reference to a function as argument. This last function argument uses the keyword this several times so I need to be scoped into my object. I solved this by doing:
MyObj.prototype.internalFunc= function(data){
this.doSomethingWithReturnedData(data);
};
MyObj.prototype.doSomething= function(){
var $this = this;
externalFunction(this.someVar, function(data){ $this.internalFunc(data); });
};
var objInst = new MyObj();
objInst.doSomething();
I want to know if there is away to avoid this messy var $this = this thing.
I also thoung of:
//... (same as before)
MyObj.prototype.doSomething= function(){
var $this = this;
externalFunction(this.someVar, this.internalFunc, this);
};
//... (same as before)
with
function externalFunction(arg1, cbfunc, thisref){
//bla bla things
cbfunc.call(thisref, blablaData);
};
but I also find it messy.
I think there is a better way of doing it and I'm not seeing it! Thanks in advance!

There is one other option available to you, if you're on a recent enough browser and that is to use Function.bind.
This allows you to create a function that has been bound to a specific scope. Put another way, it allows you to define what this will be, inside that function. Thus, you could do it this way.
MyObj.prototype.doSomething= function(){
externalFunction(this.someVar, this.internalFunc.bind(this, data));
};
Follow the link above and scroll to the bottom to see information about browser support.
Edit
Actually, there's one other option, which is to use one of the many libraries and frameworks that are out there. Any one worth its salt will have a bind, hitch, proxy or otherwise available to you. However, all these are doing are wrapping up the native JS approaches, but they often provide useful additions that make this technique even more valuable.

Assigning this to a local variable is fine and common.
Many libraries make your second approach easier by providing a method that returns a wrapper function and sets the execution context, e.g. $.proxy in jQuery (or _.bind in underscore.js).

Related

Es6: How to pass this when i use callbacks? [duplicate]

I'm inside a function of an object and I need to call an external function and pass a reference to a function as argument. This last function argument uses the keyword this several times so I need to be scoped into my object. I solved this by doing:
MyObj.prototype.internalFunc= function(data){
this.doSomethingWithReturnedData(data);
};
MyObj.prototype.doSomething= function(){
var $this = this;
externalFunction(this.someVar, function(data){ $this.internalFunc(data); });
};
var objInst = new MyObj();
objInst.doSomething();
I want to know if there is away to avoid this messy var $this = this thing.
I also thoung of:
//... (same as before)
MyObj.prototype.doSomething= function(){
var $this = this;
externalFunction(this.someVar, this.internalFunc, this);
};
//... (same as before)
with
function externalFunction(arg1, cbfunc, thisref){
//bla bla things
cbfunc.call(thisref, blablaData);
};
but I also find it messy.
I think there is a better way of doing it and I'm not seeing it! Thanks in advance!
There is one other option available to you, if you're on a recent enough browser and that is to use Function.bind.
This allows you to create a function that has been bound to a specific scope. Put another way, it allows you to define what this will be, inside that function. Thus, you could do it this way.
MyObj.prototype.doSomething= function(){
externalFunction(this.someVar, this.internalFunc.bind(this, data));
};
Follow the link above and scroll to the bottom to see information about browser support.
Edit
Actually, there's one other option, which is to use one of the many libraries and frameworks that are out there. Any one worth its salt will have a bind, hitch, proxy or otherwise available to you. However, all these are doing are wrapping up the native JS approaches, but they often provide useful additions that make this technique even more valuable.
Assigning this to a local variable is fine and common.
Many libraries make your second approach easier by providing a method that returns a wrapper function and sets the execution context, e.g. $.proxy in jQuery (or _.bind in underscore.js).

What's the advantage of using `var self = this` in knockout.js view models [duplicate]

This question already has answers here:
var self = this?
(8 answers)
Closed 9 years ago.
I see in almost all examples of a knockout.js viewmodels the line var self = this and then all local variables are references as self.variableName. What is the advantage of this over using this.variableName?
Normally the main reason for using this approach is to make the current this available to subfunctions or closures. For example:
var myObject = {
param: 123,
method: function(){
alert( this.param );
},
method2: function(){
setTimeout(function(){
alert( this.param );
},100);
}
}
In the above calling myObject.method will give you the correct alert of 123. However calling myObject.method2 will give you undefined. This is because this inside the anonymous function used with setTimeout does not refer to myObject, depending on the JavaScript interpreter it will point to different things. However, if you have:
method2: function(){
var self = this;
setTimeout(function(){
alert( self.param );
},100);
}
This works because the current state of this — at the right point — is captured, and will always reference myObject for every function scope that it is available to.
The problem is not limited to the use of setTimeout. At any point where you have anonymous functions, subfunctions or closures this trick will come in handy. Sometimes people use self, or that or something a bit more descriptive depending on what the current reference represents.
rather than storing as a variable
There is an alternative to using self or any other variable to "remember" the state of this at any particular point, and that is to "bind" your anonymous or sub functions with a particular context. Many modern interpreters now support the Function.prototype.bind method, which can be used thusly:
var method = function(){
console.log(this);
};
var methodWithWindow = method.bind(window);
var methodWithDocument = method.bind(document);
var methodWithObject = method.bind({random:"object"});
Calling each of the bound methods in turn would give you the following console output:
Window
Document
Object {random:"object"}
If you wish to support older browsers you can use a polyfill, or if you prefer a much simpler implementation, one that doesn't worry about binding arguments as well. The basics of what the bind code does is the following:
!Function.prototype.bind && (Function.prototype.bind = function(context){
var method = this;
return function(){
method.apply(context, arguments);
}
})
So, how would the initial example look using bind?
method2: function(){
setTimeout((function(){
console.log(this); // `this` will be the same as the `this` passed to bind.
}).bind(this),100);
}
As you can see above, once bound, the returned function (closure) retains that specified context; so it can be passed around where ever you want and still keep a this reference to the object you want. This is useful in the method2 example because we bundle the method up with our current context and pass it to setTimeout which will execute the bound method later (long after we have exited the current block execution).
The same does occur for when using self or any other variable. The variable would be captured within the function's scope chain, and would still be there for access when the function is eventually called again. The benefit of using bind however is that you can override the context easily if you so wish, you would have to code your own specific methods to do so to override a self variable.
WARNING: It is worth noting here that when you bind a function, a new function is returned. This can cause confusing situations if you mix bound functions with event listeners and then attempt to remove the listeners using the original function rather than the bound version.
Also, because binding returns a new function, if you bind a bound function you are in fact wrapping a function in a function, with another function. You should be aware of this because it affects performance and will prove trickier to manage in terms of avoiding memory leaks. My preferred approach to binding is to use closures with their own deconstruction methods (i.e. rely on self, but make sure you have methods to nullify it's content), but this does take more forward thinking and is not so relevant in smaller JS projects; or one off function bindings — especially if the bound method is never caught in any reference.
without self and bind?
It is also worth mentioning that sometimes you can achieve the same result without using bind at all, and instead use apply — which should be natively available in anything you may choose to use. The major difference being that nothing is wrapped up with the function, and calling apply actually executes the function there and then with a different context — the first argument passed to apply.
var externalMethod = function(){
console.log(this); // will output myObject when called below
};
var myObject = {
method2: function(){
externalMethod.apply(this);
}
};
What is this?
Just to elaborate this answer with further detail about this — before the recent comments get deleted. this will refer to one of four things, depending on how the function you are using it within was called:
myObject.method()
The above will have a this of myObject, unless method has had a .bind(context) operation applied. In which case this will be whatever the last bound context was.
unattachedFunction()
Will have a this of the global context (usually window in browser environments), unless unattachedFunction has had a .bind(context) operation applied. In which case this will be whatever the last bound context was.
anyFunction.apply(otherObject)
or
anyFunction.call(otherObject)
Both will always have a this of otherObject, because calling in this way will override any binding.
new myObject()
Will have a this that refers to a new instance of myObject, this will override any binding.
Simple thought experiment
Taking all the above into account, what would this be inside referencedMethod?
var referencedMethod = myObject.method;
referencedMethod();
Correct! it will be the global context. This is why if you want to share methods with other objects or code — but still retain the original owner as context — you really need to either bind, or keep the function bundled with its owner object so you can call or apply.
Self is used to make sure the original this is maintained within the object.
This comes in handy when using event handlers and so on.
You can read more about this here
The first answer covers it basically, also it shows a good link. Check it out.
It is used for reference purposes. this under Javascript behaves different than in other languages. For more details look at MDN Docs on this

Why assign `this` to `self` and run `self.method()`?

I'm reading the source from mongoose
Collection.prototype.onOpen = function () {
var self = this;
this.buffer = false;
self.doQueue();
};
I don't understand why the author assigns this to self and runs self.doQueue(). Why not just run:
this.buffer = false;
this.doQueue();
I'm new to javascript, thanks for help.
You're right, in this instance they could have simply used this.
The use of me or self is a bit of a hack to ensure the correct context of this is used, as within JavaScript the scope of this is variant. If for example you have an event trigger a function within your class, this would be different, and wouldn't be your object that houses the function, but instead the object that called the function. To resolve this people often use me or self to ensure they're referring to the correct object... this, as in the actual object.
Just to give more clarity to #richard said earlier,
Collection.prototype.onOpen = function () {
var self = this;
this.buffer = false;
this.onclick = function(){
//do some other operations here
//if you refer `this` here then, `this` will refer to present function not the above. so to make sure it is referring to exact object people pass this to `me` or `self`
self.doQueue();
}
};
The only reason you would usually do that is if the call to doQueue() is inside a block that will change the value of this such as another function.
In this case however it doesn't serve any purpose and was probably a remnant of older code that was not changed back.
Most likely the developer wanted consistency, but failed at doing so.
Otherwise you'd be using this in some functions, self in other functions and a mix of both in other functions, depending on where you use the object and if you use nested functions/callbacks.
By always assigning this to self and then using the latter you have one additional assignment at the very beginning of each function but you always use self to access the object.
However, what the developer did in the code you posted does not make much sense. He should either use self or this both times instead of a mix that is not even necessary.
self is a copy of 'this',but it always refer to the right object,and 'this' may not.

Stop holding 'this' in a temp variable

I am continually having to hold this in a temp variable in order to access it in other functions. For example in the two methods below, I am holding this in a that variable:
startTimer: function () {
var that = this;
if ($('#defaultCountdown:hidden'))
$('#defaultCountdown').show('slow');
shortly = new Date();
shortly.setSeconds(shortly.getSeconds() + 5);
$('#defaultCountdown').countdown('change', { until: shortly,
layout: '<ul id="errorList"><li>Next update in <b>{snn}</b> {desc}</li></ul>',
description: 'seconds',
onExpiry: function () {
that.performUpdate();
}
});
},
performUpdate: function () {
var that = this;
this.message.fetch({
success: function () {
$('#calleesStatuses').html('');
that.callees.refresh(that.message.get("Callees"));
$('#defaultCountdown').hide('slow');
that.startTimer();
},
error: function (request, settings) {
that.killCountDown();
showErrorMessage(request.responseText)
}
});
},
Is there anyway around this or could I possibly use apply?
ECMAScript 5 introduced Function.bind()[docs], so it is only supported by newer browsers. An alternative implementation can be found in the documentation I linked to. If you include it in your code, you can use bind() in the other (older) browsers too.
It lets you set the object this should refer to in the function. So you could write:
performUpdate: function () {
this.message.fetch({
success: function () {
$('#calleesStatuses').html('');
this.callees.refresh(this.message.get("Callees"));
$('#defaultCountdown').hide('slow');
this.startTimer();
}.bind(this),
error: function (request, settings) {
this.killCountDown();
showErrorMessage(request.responseText)
}.bind(this)
});
},
I think that's the simplest way to do it. This is what I do (although I'm writing GWT code), to reference the this of the wrapping function in an inner anonymous function.
Even if something like this.wrappingMethod.this were/are possible, storing the the this in a variable named according to your taste is a lot more readable (you could use a more descriptive name, ofcourse), and (still assuming you cold somehow reference the wrapping scope) it will be more robust since you could introduce another level without having to rewrite all the references.
No, there isn't. The value of this will be different in the closure than it is in the scope that the closure is defined so the only way to make it cleaner is to define it on an object level so at least you only have to do it once per object, which it looks like you are already doing anyway.
Edit:
Strike out the "No there isn't" because bind is a valid alternative and there are comparability implementation (see other answer). Although I personally think var self = this; is cleaner and you only need to define it once per object but it is a matter of preference at this point.
I guess it's no comfort, but not really if the original this is the only context in which you can execute functions like startTimer and killCountdown. What I'd recommend is giving it a more meaningful name, like timer or something. Really, this is just a convenient keyword for referring to the owner of whatever we're executing, and it should change as the owner changes. If "this/that" is becoming hard to read, the solution is to change the name from that to something more semantically meaningful.
What you're doing is a standard pattern in JavaScript, except that it's traditional to use self instead of that. But you can use the bind() method like this:
onExpiry: performUpdate.bind(this);
if you're working with a framework that extends the prototype of Function to include it, or your Javascript interpreter is recent enough. (bind creates the same sort of anonymous function behind the scenes, but is arguably less efficient since it has to deal with all sorts of special cases).

Naming: createFunctionDelegate() vs createDelegateFunction()?

It appears that I am not able to choose between two names for a function:
createFunctionDelegate() and createDelegateFunction().
If it matters, the purpose of the function is that it creates a new function that calls the supplied callback function in the context of the second argument. For example:
var foo = {
init: function() {
setTimeout(App.createFunctionDelegate(this.method, this));
},
method: function() {}
}
When foo.init() is run, it sets a timeout that calls a function which delegates the execution to another function (this.method) called in the context of this (foo).
Anyway, I am not sure which way I should name this function. This is important to me, because I am going to use it in hundreds of places and sometimes I type the one and occasionally the other one. This has to change, I have to choose.
I would use neither of these. What you want to do will be offered by bind() in ES5. I would define Function.prototype.bind if it does not exist already, as described here (but read the description and the possible drawbacks carfully).
This way you make sure you use native functionality if it is supported.
What about just createDelegate(this.method, this)?
As a side note, other places where I've seen this kind of method (and written them), the param ordering is context, function, so createDelegate(this, this.method)

Categories

Resources