Javascript Passing a method of an object as a function - javascript

Today I found out a rather strange behaviour that happens when you pass the method of an object as a function in Javascript.
setTimeout(myObject.test, 100); // "this" is the global object window
The method "test" is correctly invoked, but "this" is not the object "myObject" but instead the global object "window". I can get the expected behaviour when I do something like this:
setTimeout(function(){myObject.test()}, 100); // "this" is myObject
This seems to me rather strange. Can anybody explain, why this is.

Consider this pseudo-representation how JavaScript will interpret setTimeout(myObject.test, 100);
function setTimeout(func, millisec) {
//some code...
func(); //"this" in func will point to window
//some code...
}
But when you do this - setTimeout(function(){myObject.test()}, 100);, setTimeout will execute the anonymous function you passed, so this the anonymous function will still point to window, but that anonymous function calls another function myObject.test(), since, function is being called as a method of an object, this will point to that object.
You can solve this by using a cool JS function bind.
setTimeout(myObject.test.bind(myObject), 100); //now "this" will point to myObject.
A better explanation is here by a JS Guru

The run time will execute the function argument (1st argument) for setTimeout on window object, and you put the test method directly as the argument, so you see this is bond to window object in your first case.
In your second case, the function executed on window object is your anonymous function function() { myObject.test() }, however, the test method is invoked on myObject, that's why you see this is bond to myObject

Related

Calling vs invoking a function

Up to this point, I thought "calling" and "invoking" a function meant the same thing. However, in a YouTube tutorial it said to invoke a function by calling it. My first thought was that the wording was a mistake, but on W3Schools' page on Function Invocation, it says:
It is common to use the term "call a function" instead of "invoke a
function" ... In this tutorial, we will use invoke, because a
JavaScript function can be invoked without being called.
Okay, so there's a difference. What is it?
Your reference text:
It is common to use the term "call a function" instead of "invoke a
function" ... In this tutorial, we will use invoke, because a
JavaScript function can be invoked without being called.
Now let me rephrase it:
It is common to use the term "call a function" instead of "invoke a
function" ... In this tutorial, we will use the term invoke instead of call, because a
JavaScript function can be invoked indirectly like fn.call() and fn.apply() without being called directly like fn().
So, when I do fn(), it's invoked directly and when I do it like fn.call(), it's invoked indirectly but in both cases, the function is being invoked. Otherwise, I see no difference here and I can also say that I can call a function in many ways, for example:
fn(); // I'm calling it
fn.call(); // I'm calling it
fn.apply(); // I'm calling it
So, the difference is semantic but both are interchangeable, IMO. BTW, I wrote a comment above, under the question and I would like to put it here, which is:
IMO, that's a misleading statement. Maybe there are some indication of
call/apply or something else but it's totally confusing.
The difference is semantic and subtle. When you call a function, you are directly telling it to run. When you invoke a function, you are letting something run it.
There is one way to call a function:
myFunction()
Here, you are invoking the function (letting it run) by calling it directly.
There are many ways to invoke a function (given throughout different comments and answers). Here's one example:
function invoker(functionName) {
functionName()
}
invoker(myFunction)
Here, by calling invoker, you are invoking myFunction, which is being called indirectly.
Yes, in most cases we use both to refer the execution of a function.
There are 2 ways to reach place B from your HOME.
Direct/Automatic way (Invoke), i.e. if you choose first way, you do not need to walk. Someone will automatically take you to place B.
Indirect way (Call), i.e. if choose second way, you only need to reach A(by walk). Someone is there at place A to automatically take you to place B.
Have a look at the below image. I think it will clear your doubt.
In case of Calling, you originally refer to the statement that actually calls the function.
In case of Invoking, you indirectly refer to calling statement to actually invoke/run the function.
Many people use the term calling and invoking interchangeably but that's not right. There is a very slight difference between calling and invoking a function. In JavaScript functions can be invoked without being called which means that the code inside the body of the function can be executed without creating an object for the same. It is tied to the global object. When there is no individual object, the value of this is associated with the global object.
There is also a difference between call and apply, the fundamental difference is that call() accepts an argument list, while apply() accepts a single array of arguments. A different this object can be assigned when calling an existing function. this refers to the current object, the calling object. With call, you can write a method once and then inherit it in another object, without having to rewrite the method for the new object.
So, the major difference between invoking and calling comes in terms of the this object. Calling let's you set the this value whereas invoking just ties it to the global object.
"function invoked" means a function got executed
"function called" means that a function was called by another function and then executed
"function invoked without being called" is a function that got self invoked without being called by another function
example of a self invoking function calling another function:
var f = (function() {
foo();
})(); ///() here means it self invoked -- without being called directly.
var foo = (function() {
///Do something here
});

'this' concept in the object in javascript

I am sorry for that the simple question first. But I just want to know what happen in the code below.
var indexobj = {
test : 1,
testFunction : function() {
document.getElementById('AA').innerHTML = indexobj.test++;
//this is i wanted
document.getElementById('BB').innerHTML = this.test++;
//NaN
setTimeout(indexobj.testFunction,1000);
},
}
setTimeout(indexobj.testFunction,1);
Here is the sample link below
http://jsfiddle.net/apss/JR5Xk/28/
Why in this example the ‘this’ inside the ‘testFunction’ function doesn’t mean ‘indexobj’ this object?
Thanks for your help.
Because setTimeout runs callback function in global object context, meaning that indexobj.testFunction is invoked with this being a window object.
So you can either do
setTimeout(function() {
indexobj.testFunction();
},1000);
or using Function.prototype.bind to create wrapper function with correct context:
setTimeout(indexobj.testFunction.bind(indexobj), 1000);
Another modern option from ES2015 is to use arrow function that preserves lexical context:
setTimeout(() => indexobj.testFunction(), 1000);
A function's this set set by:
How the function is called
the use of bind.
I doesn't matter where it's called from.
In this case, you have:
setTimeout(indexobj.testFunction,1000);
which passes the function referenced by indexobj.testFunction to be called later by setTimeout. When called, the function is called directly, so its this isn't set by the call and it defaults to the global object (window in a browser) or remains undefined in strict mode.
You can fix that in a number of ways, one is to pass a function that sets indexobj to this in the call:
setTimeout(function(){indexobj.testFunction()}, 1000);
or you can use bind as suggested by dfsq.
Arrow functions in the ES6 draft change how this is set, but that's for the next edition…

Why does this refer to window or object based on where parenthesis are placed? And return undefined

I am working on flexible javascript widget. In the course of some testing, I noticed that the context of this changes based on whether the parenthesis used to call a function are placed inside an object or in the global context of its call. Why do the parenthesis matter? And why would parenthesis inside the object refer to window and when placed in global context refer to the object. It seems like it would be the other way around.
Also undefined is returned in both instances. Is there a way to execute the function without returning anything?
I feel like I'm missing something important about this and don't want to miss out.
//this refers to window
var dataSource = {
read: read()
};
function read(){
console.log(this);
}
dataSource.read;
//this refers to dataSource object
var dataSource = {
read: read
};
function read(){
console.log(this);
}
dataSource.read();
Your code is doing two different things.
The first example is executing read() as the object definition is executed (read() is available because it's a function declaration and is hoisted, though this isn't related to the problem you're experiencing). It is called without any context so its this is window (as per the specification, where window is the browser's global object).
The second example has a reference to read(), which is then executed at the end of the block. Because it's executed as a property of dataSource, its this will become that. However, if you first assigned that reference to somewhere else and then invoked it via that reference, you'd again lose that this context.
For fine-grained control of this, take a look at bind(), call() and apply().
Also undefined is returned in both instances. Is there a way to execute the function without returning anything?
A function always has a return value (undefined if not explicitly set), but you're free to ignore it.
The scoping of this can be a tricky topic in javascript. That said, I can expand my answer on the general rules regarding the scope of this if need be.
But to answer your specific question, whenever you reference this inside an object literal, it by default, refers to the object literal itself.
Edit: as long as the function was invoked as a property of the object literal.
Where as, almost in any other situation I can call to mind, this will refer to the window object unless specified when invoking said function using apply() or call().
When this is used outside of objects it refers to the global object which in browser environment is window. Otherwise it refers to the last bareword before the last dot in the invocation.
For example:
function foo () {return this};
var bin = {bar:{foo:foo},foo:foo};
foo(); // returns window
bin.foo(); // returns bin
bin.bar.foo(); // returns bar
// ^
// |
// '------ last bareword before the last dot in the invocation
Now, as to why the location of the parenthesis matter. I think you should be able to guess by now:
When we add a parenthesis to a word (variable/name/reference) what we're doing is make a function call:
foo(); // call foo
If we don't add the parenthesis, what we're doing is refer to the object:
foo; // this contains the function foo
Note that not adding the parens is not calling the function. Therefore it should be obvious that when you do:
var bar = { foofoo : foo() }
What you're doing is passing the result of the function foo to bar.foofoo. The function is invoked without any "dots" in its invocation path. Therefore it doesn't belong to any object and therefore the rule of this == window applies.
On the other hand if you do:
var bar = { foo : foo }
What you're doing is assign the function foo to bar.foo. When you later call it as:
bar.foo()
the invocation contains a "dot" therefore the rule about the last object before the last dot applies.
See my previous answer to a related question for a detailed explanation on how this works in javascript: How does the "this" keyword in Javascript act within an object literal?

How do the following Javascript functions affect the value of 'this'?

I have an object which has two functions within it, and as I guessed each one has a different value for this:
custom_controls : {
play_pause : function () {
console.log(this); // object
videoPlayer.getIsPlaying(function (video_is_playing) {
if (video_is_playing) {
console.log(this); // window
videoPlayer.pause(true);
} else {
videoPlayer.play();
}
});
}
},
Then the function is invoked like this:
custom_controls.play_pause()
I've heard that the way you invoke a function denotes the value of this.
So my question is: What is happening here? What kind of function invocations am I using? And how does each one affect this?
When calling obj.func(), this inside the function will be equal to obj. If there is no obj, the global object (window) is used instead. Or if you are running in Strict Mode, undefined is used.
The first log is your object because you call the function like this:
custom_controls.play_pause() // custom_controls will be 'this'
The second log is window because the function passed as parameter to getIsPlaying is not called with any this:
videoPlayer.getIsPlaying = function(callback) {
callback(); // this inside callback will be window
}
You can control what the value of this will be when you invoke a function by using call or apply. You can create a new function which will always have the this value set to whatever you want by using the bind function:
videoPlayer.getIsPlaying(function (video_is_playing) {
if (video_is_playing) {
console.log(this); // my obj
videoPlayer.pause(true);
} else {
videoPlayer.play();
}
}.bind(this)); // magic!
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Each function is actually executed within a context. That context is denoted as the current this for which you call the function.
Given your code:
If you call custom_controls.play_pause() you are saying "Take the field of the object custom_controls named play_pause and execute it within the context of the object custom_controls".
Later on calling videoPlayer.getIsPlaying() means pretty much the same. Except you're giving it a callback function. How that callback function is executed later on depends on how videoPlayer.getIsPlaying is implemented.
If I have to guess I'd say that getIsPlaying has a callback.call(window, video_is_playing) somewhere in it.
call is a method of all function objects in javascript.
There are a few ways to work around this "issue" if you want to reference a this in some callback.
var self = this;
call_me_maybe(function() {
console.log(this); //the this that call_me_maybe chose to call your function with
console.log(self); //the this from the upper scope
});
or if you don't care about the object in which context call_me_maybe will call your function:
call_me_maybe((function(){
console.log(this); //the this from the upper scope
}).bind(this));
What bind does is it returns a wrap[per of the function which will always be called in the context of the object to which it is bound.
bind can also bind arguments as well as the this object for the function, creating a sort of curry.
Your first this is referring to play_pause.
Your second this could either be referring to the Window or your videoPlayer object. In JavaScript, closures and regular functions are 'generally' attached to window, and calling this returns window. In certain cases, e.g. if you attach a function to the click handler of an HTML element, this refers to the element...
element.onclick = function(){
this // -> element
}
But generally if you just create a function(), or have an anonymous one like yours this refers to window.
function hello(){
this // -> window
}
The this that you've discovered is the object is what you would expect because the function is operating on that object.
I'm not familiar with your videoPlayer, but as the value of this is the "window", I would imagine that either A the video player is a function of the browser itself, or B its scope was not properly closed.
this refers to the "proprietary" of the function, or the object from the function is a method of.
When you define a basic function, the "proprietary" is the page (or the window) itself.
You can check the callback documentation for workarounds
When you call videoPlayer.getIsPlaying it accepts a callback fn. Callback is invoked directly like cb() and hence the context is global (window).
To achieve the callback happening in context of your object. You can use
var fn = cb.bind(customControl);
videoPlayer.getIsPlaying(fn);
As a rule of thumb when a function is called like object.function this is set to object. If function is called directly, this is set to window. Function.bind return a function after binding object (this value) optionally along with parameters.
Read: MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Dojo.hitch() scope question

Why is it that when I use the dojo.hitch function and try to reference the "this" operator inside it, that it gives me reference to the wrong object?
console.debug(dijit.byId("widgetName")); //works as expected and prints out the window object
dojo.hitch(dijit.byId("widgetName"), tester())(); //this should be executing the tester function in the scope of the widget object
function tester() {
console.debug(this); //prints out the Javascript Window object instead of the widget object!!!!
}
Thanks
Based on what you have just shown, I can now safely provide an answer explaining what is wrong.
When you do a dojo.hitch() you should not call the function inside of it, but instead call the result of the function. That is, you need to provide dojo.hitch with a reference to the function to hitch, not the result of invoking that function.
In your example, you are calling tester() (which invokes the function tester) inside of the dojo.hitch(), which calls the tester once. Even though you have dojo.hitch()(); because tester() does not return a function handler (but the result of tester, in this case undefined), the hitch()(); does nothing. That might have been confusing so I will show you with an example.
Don't do this:
dojo.hitch( context, handler() )();
Instead do this:
dojo.hitch( context, handler )();
So to make what you have very readable you would do this:
widget = dijit.byId("widgetName");
tester = function() {
console.log(this);
}
handle = dojo.hitch(widget, tester);
handle();
Your mistake was trying to call the function from within the dojo.hitch(). This mistake also was not present in your original question.

Categories

Resources