I've been trying to learn about closures, but one thing still perplexes me. If I have the following code:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
// Returns "3"
If I call add() three times, why dosen't it set counter to zero every time, then return the anonymous funtion that increments counter by one? Does it skip over it once the self-invoking function runs? Sorry if the question seems simple, I'm having a hard time understanding it. Any help would be greatly appreciated.
If I call add() three times, why dosen't it set counter to zero every time, then return the anonymous funtion that increments counter by one?
Because add is that anonymous function, because the function containing counter got called and its result was assigned to add:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
//^^----------- calls the outer function, returns the anonymous inner function
If you didn't call it:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
});
//^--- no () here
...then add would do what you said, it would return a new function with its own counter, each time you called it:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
});
var a = add();
var b = add();
var c = add();
console.log("a's first call: " + a());
console.log("a's second call: " + a());
console.log("a's third call: " + a());
console.log("b's first call: " + b());
console.log("b's second call: " + b());
console.log("b's third call: " + b());
console.log("a's fourth call: " + a());
console.log("b's fourth call: " + b());
.as-console-wrapper {
max-height: 100% !important;
}
That's not resetting counter, that's creating a new counter each time.
By calling add() you are not actually executing outer function but instead you are executing inner function. For inner functtion, counter is like a global variable that has been set once to 0 and then it was never set again to 0. On calling add() you are executing lines inside inner function thus increamenting counter.
The value assigned to add is the result of the IIFE, in which the closure was created. Maybe it's more obvious what will happen when add() is called, when its creating is written as follows (equivalent to your original code):
var add;
(function () {
var counter = 0;
add = function () {return counter += 1;};
})();
Related
I have referred to multiple links for this concept but it is little bit tricky to wrap the concept around the head completely.
I was going through and example of it on https://www.w3schools.com/js/js_function_closures.asp
var add = (function () {
var counter = 0;
return function () {counter += 1; return counter}
})();
add();
add();
add(); //Counter is 3
But isn't every time this is getting called counter is getting reinitialized to 0?
Can someone use table or something to help me understand every step here?
What add holds is returned function in the IIFE. (https://developer.mozilla.org/en-US/docs/Glossary/IIFE)
function () {counter += 1; return counter}
So calling add doesn't reinitialize counter.
var add = (function() {
var counter = 0;
return function() {
counter += 1;
return counter
}
})();
console.log(add());
console.log(add());
console.log(add());
var addHandler = function() {
var counter = 0;
return function() {
counter += 1;
return counter
}
};
const add = addHandler(); // return inner function
console.log(add());
console.log(add());
console.log(add());
In javascript, functions have their own execution environment.
And the execution environment is created when the function is called and disappears when execution is complete.
In the code above, the value in "add" becomes the new function returned after the IIFE function is executed.
In other words, the execution environment of the IIFE function has already been executed and disappeared.
However, the inner returned function is still looking at the parent function's variable "counter".
Previously, it was said that the environment disappears when the function is called. In this case, the remembering the environment of the parent function is called a'closure'.
IIFE is an immediate function.
Functions are declared and called when necessary in the form of "function name()".
The IIFE above will execute the function right away by declaring the function and wrapping it in "()".
This question already has answers here:
Why do javascript variables in closure functions not reset to a default when called multiple times?
(2 answers)
Closed 2 years ago.
Simple JS logic question here.. In the below code, why does add() only set counter to 0 once? I feel like every time the function is called it should reset it to zero.
var add = (function () {
var counter = 0;
return function () {counter += 1; return counter}
})();
add(); // 1
add(); // 2
Thanks!
You're misunderstanding the code. The function add() does not contain the code var counter = 0.
Here's a rewrite of the exact same code that makes it more clear:
var add;
// Note: THIS function is NOT add()
(function () {
var counter = 0;
// THIS function is add()
add = function () {counter += 1; return counter}
})();
add(); // 1
add(); // 2
The code above does exactly the same thing as your original code except for how add was assigned. In your code it was assigned via a return value but in the above I simply assigned it directly as a global variable to make it more clear which function is add().
Another way to look at it that is more like your original code is to explicitly name the two functions:
var function1 = function () {
var counter = 0;
// The returned function is what will be assigned to add()
return function () {counter += 1; return counter}
}; // Do not call it yet since calling it was what was confusing you
var add = function1();
add(); // 1
add(); // 2
Why does the variable counter keep increasing when it re-initializes itself back to 0 in the self-invoking function?
var add = (function () {
var counter = 0;
return function () {
return counter += 1;
}
})();
add();
add();
add();
// the counter is still 3 when this line of code exists
// var counter = 0;
So when add() function gets invoked, a function runs first with initializing counter = 0 and returning another function that returns the incremental value of counter. Why is this the case when there's var counter = 0; ?
source: https://www.w3schools.com/js/js_function_closures.asp
edit: November 16th, 2017
Okay so it makes a lot more sense now from both of the answers given on this page. For extra clarification, I will add my own reasoning to give it a better visualization of why this happens.
//first call of add()
(function(){ // a self-invoking function
var counter = 0; // executes once with the self-invoking function
return function() { return counter += 1; } //explained below
});
//since the self-invoke function ran already, add() will begin to run the returned function:
add = function() { return counter += 1; };
add(); ----> counter += 1; -----> add.counter = 1;
//counter acts like this.counter, so it's a part of add()
//counter is now at 1
//second call of add();
//add has now officially been **transformed** to the new function:
add = function() {
return counter += 1;
};
//while add.counter still **exists** and remains in the self-invoke
//function. Do we call this **limbo variable** ? Don't know.
//add.counter is at 2
//third call of add();
function() {
return counter += 1;
}
//add.counter is at 3!
Please note that "var add" assigned a self invoking function that returns a function.
Whatever above the return statement is the self invoking function closure. Which means that it is available to the returned function.
So it is in fact only initialized once. And then the return function, which is what the variable "add" now contains, increments the counter whenever it is invoked.
Because the variable add contains the result of the self invoking function. So the value of add is this:
function () {
return counter += 1;
}
The counter variable is only initialized before this function is set to the add variable.
I am creating two function name call and call2 and putting this two functions in array x. In another function called timer i am trying to call this function every 2 seconds. But its giving error expected an assigment or function call instead show and expression.
And also i dont want this functions to run when i create an array.
this my code http://jsbin.com/IMiVadE/2/
function call(name)
{
console.log("Hello " + name);
}
function call2()
{
console.log("Hello world");
}
var x = [call("Nakib"), call2()];
var i = 0;
function timer(x, i)
{
x[i];
i++;
if(i<x.length)
window.setTimeout(timer(x, i), 2000);
}
timer(x, i);
You have some mistakes in your code:
call2() calls the function. Correct: call2 is the reference to the function.
x[i] accesses the value (the function reference). You need x[i]() to execute the function.
Here is your code working: http://jsbin.com/IMiVadE/6/edit
sayHello(name) is a function that generates new functions that output a specific "Hello ..." text.
function sayHello(name)
{
return function () {
console.log("Hello " + name);
}
}
function timer(x, i)
{
x[i]();
if (i < x.length - 1) {
setTimeout(function () {
timer(x, i + 1);
}, 2000);
}
}
var x = [sayHello("Nakib"), sayHello("world")];
setTimeout(function () {
timer(x, 0);
}, 2000);
setTimeout needs a function without parameter. So you can wrap the recursive call to timer in an anonymous function to fix that:
window.setTimeout(function(){timer(x, i)}, 2000);
Also, the first line in your timer function, consisting only of x[i];, is useless, although it probably isn't the cause of your problem.
The setTimeout function takes a function as a parameter, you are executing the timer function before it is passed and since timer doesn't return anything, undefined is being passed to the timeout;
window.setTimeout(timer(x, i), 2000); // is the same as...
window.setTimeout(undefined, 2000);
It should be;
window.setTimeout(function() { timer(x, i) }, 2000); // or...
window.setTimeout(timer.bind(this, x, i), 2000);
I'm not sure if this is intentional, but you are doing the same thing with your array;
var x = [call("Nakib"), call2()];
This will execute the functions and their results will be stored in the array. Is this what you want?
Perhaps an odd question but here it goes: I have a function which I call periodically and within that function I need to know which iteration I'm in, or how many times the function has been called. A simplified version of the problem:
jQuery( document ).ready( function(){
setInterval( "myFunction()", 3000 );
});
function myFunction()
{
alert( "I have been called X times" );
}
So, how do I figure out the X in the above code?
Easy version: make a global variable like in codeling's answer. The problem - if some other code also defines a global variable with the same name, you're both in trouble.
Easy extended version - give the variable a crazy name that nobody will ever use: calledTimesED7E69A7B141457CA8908A612E3D7A3A
Clever version: append that variable to an existing global variable. Remember - everything's an object in Javascript!
$(function(){ setInterval(myFunction, 3000); });
function myFunction()
{
myFunction.calledTimes++;
alert( "I have been called " + myFunction.calledTimes + " times" );
}
myFunction.calledTimes = 0;
Traditional version: use scoping to hide that variable.
$(function()
{
var calledTimes = 0;
setInterval(function()
{
calledTimes++;
alert( "I have been called " + calledTimes + " times" );
}, 3000);
});
This hides "myFunction" though, so let's try again with a tricky kind of scoping:
var myFunction = null;
(function()
{
var calledTimes = 0;
myFunction = function()
{
calledTimes++;
alert( "I have been called " + calledTimes + " times" );
}
})();
$(function () { setInterval(myFunction, 3000); });
... and there are a zillion other ways you would hide that variable with scoping. Just pick your favorite.
You could simply use a global variable, which is increased each time you call the function:
var myFuncCalls = 0;
function myFunction()
{
myFuncCalls++;
alert( "I have been called " + myFuncCalls + " times" );
}
As soon as your code gets a little more complex (or if you use a lot of other libraries), you should, however, consider using scoping as shown in the other answers here (best explained in the one by Vilx).
Here's another interesting solution that doesn't use an external variable. The best part about this is you can leave any pre-existing functions untouched and call them as you would normally. That means if you're attempting to "tap in" to an function in an existing library, this will work very well for you. It adds an unobtrusive counter and allows you to continue calling existing functions normally; even with arguments!
// no js library required
// pre-existing function
var a = function(){
console.log("pre-existing function function");
console.log("arguments:", arguments);
};
// add counter func
var addFnCounter = function(target){
var swap = target;
var count = 0;
return function(){
swap.apply(null, arguments);
count++;
console.log("func has been called " + count + " times");
console.log("\n");
};
};
// usage
a = addFnCounter(a);
// call a() as you would normally
a();
a(1,2,3);
a('hello', 'world');
// using your setInterval example
setInterval(a, 3000);
Output
pre-existing function function
arguments: []
func has been called 1 times
pre-existing function function
arguments: [1, 2, 3]
func has been called 2 times
pre-existing function function
arguments: ["hello", "world"]
func has been called 3 times
setInterval output
pre-existing function function
arguments: []
func has been called 4 times
pre-existing function function
arguments: []
func has been called 5 times
pre-existing function function
arguments: []
func has been called 6 times
See it working here on jsfiddle
You'll have to use a closure.
Normally you would use a static variable. in Javascript it would look something like:
jQuery( document ).ready( function(){
setInterval( myFunction, 3000 );
});
var myFunction = (function(){
var count = 0;
return function(){
count++
alert( "I have been called " + count + " times");
}
})();
Demonstration: http://jsfiddle.net/MZQ83/2/
A static variable is cleaner and it won't pollute your outer scope either, compared to a closure or a decorator as in other answers.
var foo = function(){
alert( ++foo.count || (foo.count = 1) );
}
// test
function callTwice(f){ f(); f(); }
callTwice(foo) // will alert 1 then 2
or
callTwice( function bar(){
alert( ++bar.count || (bar.count = 1) );
}); // will alert 1 then 2
the second one is a named anonymous function. And note this syntax:
var foo = function bar(){ /* foo === bar in here */ }
Create a global variable and initialize by zero. then increment by one when myfunction() called. Display that variable instead of X.
ES6 / ES2015
You can use a Proxy for your function utilising the apply() trap:
const addCounter = fn => {
let count = 0; // keep count
//define handler
const handler = {
apply() {
//do something with this counter
console.log(`I have been called ${++count} times `);
return Reflect.apply(...arguments); //call the function normally
}
}
//wrap the function into a proxy that uses the handler and return it
return new Proxy(fn, handler);
}
setInterval( addCounter(myFunction), 1000 );
function myFunction() { //sample operation - move an image
const img = document.querySelector("img");
let offset = img.offsetLeft + 10;
if (offset > 100) //return to start
offset = 0;
img.style.left = `${offset}px`;
}
img {
position: absolute;
}
.as-console-wrapper {
max-height: 45px !important;
}
<img src="https://picsum.photos/150" />
You can use an Immediately Invoking Function Expression (or IIFE) to create a closure around the counter function. You can do it like this with ES6 Arrow functions:
const counterFunction = (() => {
let counter = 0;
return () => console.log(++counter);
})();
counterFunction();
counterFunction();
counterFunction();
Or with normal function expressions:
var counterFunction = (function() {
var counter = 0;
return function() {
console.log(++counter);
};
})();
counterFunction();
counterFunction();
counterFunction();
Read more about IIFEs
Read more about closures
There is an inbuilt function in JS called console.count()
My approach would add a property “count” to the function itself.
Just add one line at the beginning of your function you want to have tracked calls.
function func() {
func.count = (func.count || 0) + 1;
// first time you call the function func.count is undefined so use 0 instead
console.log("hi");
}
func();
console.log(func.count) // 1
func();
func();
func();
console.log(func.count) // 4
Functions are objects in javascript after all. No pollution of global namespace, no wrapping or closures needed, very simple to understand and to write.