var f = function() {
this.x = 5;
(function() {
this.x = 3;
})();
console.log(this.x);
};
f.call(f);
f();
f.call();
Running var f
as f.call(f)
outputs 5
. When running it as f()
or f.call()
outputs 3
.
What happens in each case? What does the inner function's this
refer to?
Calling a function from within itself is called recursion and the simple answer is, yes.
To call a function inside another function, define the inner function inside the outer function and invoke it. When using the function keyword, the function gets hoisted to the top of the scope and can be called from anywhere inside of the outer function.
Therefore, you can easily write a function hook by using the apply() method. For instance, we want to add a feature to jQuerys . css() method. We can store the original function reference, overwrite the function with custom code and call the stored function.
Context in JavaScript is related to objects. It refers to the object within the function being executed. this refers to the object that the function is executing in.
In the first you are calling the function. And inside the function the function itself i.e f
is set as this
. So in first example this.x = 5;
sets the property x
on the function.
When the inner function is called this
refers to window
object so this.x = 3;
changes the x
property of window object.
When it logs console.log(this.x);
here the same property x
which was set as property of function is logged.
In the second example this
inside the outer function refers to window
so when this.x = 3;
is evaluated the window.x
becomes 3
. As this
refers to window
in outer function so console.log(this.x);
logs window.x
which is 3
The conclusion of the whole discussion is that if no argument is passed to call()
then automatically window
object is binded. According to MDN
thisArg
Optional. The value of this provided for the call to a function. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode, null and undefined will be replaced with the global object and primitive values will be converted to objects.
See the below snippet.
function foo(){
console.log(this);
}
foo.call(foo); //foo function
foo.call(); //window object
If there is no specific context, this
will be window
. Your inner function always runs without a context, so it'll set window.x
to 3. If you call f()
, it will also run with this
being window
therefore logging the 3.
If you however do f.call(f)
, this
will be the f
function object, and it's x
property will be set to 5.
f.call(f)
console.log(
f.x, // 5
window.x // 3
);
I'd recommend stepping through it with the debugger if it isn't clear yet.
When you call f
with a reference to itself, it sets the x
property to 5 on the function and the inner anonymous function has its this
referring to the window, so it sets window.x to 3. Outside the anonymous function, this
still refers to the function f
, so console.log(this.x) outputs 5.
When you invoke f using f()
or f.call()
the function f and the anonymous function inside it have a this
reference set to the window
(the default) and so changing the value of this.x
inside or outside the anonymous function affects the output result.
You can see this clearly if you console.log the values of this
inside the function f and inside the inner anonymous function.
var f = function() {
console.log("This inside function f:", this.toString());
this.x = 5;
(function() {
console.log("This inside anonymous inner function:", this.toString());
this.x = 3;
})();
console.log(this.x);
};
console.log("calling function x with this set to itself");
f.call(f);
console.log("---------------")
console.log("invoking function x with brackets ()")
f();
console.log("---------------")
console.log("calling function x without setting this context")
f.call();
console.log("---------------")
Further to the other answers, should you want predictable behaviour, you have at least 2 methods available to you.
Method 1: (closure)
var f = function() {
this.x = 5;
var that = this;
(function() {
that.x = 3;
})();
console.log(this.x);
};
f.call(f); // 3
f(); // 3
f.call(); // 3
Method 2: (arrow function)
var f = () => {
this.x = 5;
(function() {
this.x = 3;
})();
console.log(this.x);
};
f.call(f); // 3
f(); // 3
f.call(); // 3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With