I am trying to get around understanding javascript closures from a practical scenario.I know from a theoretical perspective , With the help of closures inner functions can have access to the variables in the enclosing function i.e parent function.
I have read a couple of questions on stackOverflow as well.
i am really missing the point of what is happening here?
var foo = [];
for(var i=0;i<10;i++){
foo[i] = function(){
return i;
}
}
console.log(foo[0]());
This gives me out a 10. Most of the articles say that by the time it reaches the inner anonymous function, The for loop is getting executed as a result the last value that is present in the loop which is 10 is being printed.
But i am still not able to get to the bottom of this.
On Contrary, If i use something like:
var foo = [];
for(var i=0;i<10;i++){
(function(){
var y =i;
foo[i] = function(){
return y;
}
})();
}
console.log(foo[0]());
I am getting the output.Any help would be highly appreciated.
maybe this code block helps
var foo = [];
for(var i = 0; i < 10; i++) {
foo[i] = function() {
return i; // is a reference and will always be the value, which 'i' have on function execution
}
}
// 'i' is 10 here!
console.log(foo[0]()); // executing the function will return the current value of 'i'
///////////////////////////////////////
var foo = [];
for(var i=0;i<10;i++) {
/* thats a IIFE (immediately invoked function expression) */
(function(a) { // 'a' is now a local variable
foo[a] = function() { // defines a function
return a; // is a reference to local variable 'a'
};
})(i); // <- passing the current value of i as parameter to the invoked function
}
// 'i' is 10 here
console.log(foo[0]()); // returns the reference to 'a' within the same scope, where the function was defined
In your first scenario, all of your functions added to the foo array are referencing the same var i. All functions will return whatever i was set to last, which is 10 because during the last iteration of the loop that's what it's value was set to.
In the second scenario, you are Immediately Invoking this function:
(function(){
var y =i;
foo[i] = function(){
return y;
}
})();
By immediately invoking it you are effectively locking in the local state of var y, for each iteration of the loop - it provides a unique scope for each function added to the array.
Related
My question is based on two premises: (1) codes are not executed until the function containing them is called and (2) variables are saved in memory.
So, are variables initialized inside function declaration always re-initialized when the function execute?
To demonstrate, if I initialize a variable inside function declaration to a huge object and call that function, then, the huge object will be created, saved, or and processed in memory. If I call or execute that function many times, then the huge object will be created, saved, or and processed everytime the function executes. Then, there will be so many and big processing of that variable (containing that huge object, and if only one). Therefore, this behavior will result in bad effect for performance. I do not know much about this, is this correct?
This is to demonstrate with code:
If I declare global variables, then the process only involve value-changing. But, if I declare local variable like this:
var hugeObj = {
prop1 : function() {...},
prop2 : [...],
prop3 : {...},
};
and I execute the containing function five times, then there will be five "hugeObj" (with different contexts) in memory (and involve more processing).
Can you explain how variables are processed inside function declarations, and will them be created in every execution context?
The answer is yes, each time the function is run it's internal variables are re-initialized and are not reused.
function MyFunc(newValue){
var v;
if(newValue) v = newValue;
return v;
};
console.log(MyFunc()); // undefined
console.log(MyFunc(5)); // 5
console.log(MyFunc()); // undefined
// Therefore we can conclude that the object is re-initialized each time the function is run and its previous variable values are not reused.
If you would like it to not initialize the variable each time, just initialize the variable globally.
var v;
function MyFunc(newValue){
if(newValue) v = newValue;
return v;
};
console.log(MyFunc()); // undefined
console.log(MyFunc(5)); // 5
console.log(MyFunc()); // 5
In addition to the answer of #DustinPoissant, you can declare different scope (local or global) inside a function.
Let's use the same code as the answer:
function MyFunc(newValue){
var v;
if(newValue) v = newValue;
return v;
};
console.log(MyFunc()); // undefined
console.log(MyFunc(5)); // 5
console.log(MyFunc()); // undefined
In this case the scope is the local function, but if you remvoe var keyword, your scope changes to global (yes, v variable will be attached to window object):
function MyFunc(newValue){
if(newValue) v = newValue;
return v;
};
console.log(MyFunc()); // undefined
console.log(MyFunc(5)); // 5
console.log(MyFunc()); // 5
That's the same as write:
function MyFunc(newValue){
if(newValue) window.v = newValue;
return window.v;
};
console.log(MyFunc()); // undefined
console.log(MyFunc(5)); // 5
console.log(MyFunc()); // 5
Because one of the variables is stored externally, run both, one will maintain its values, the one with the variable initialised within the function is overwritten.
var array = ["1","2","3","4","5"];
function MyFunc()
{
var current = "0";
console.log("Iteration");
for(var i = 0; i < array.length; i++)
{
current += array[i];
console.log(current);
}
};
console.log(MyFunc());
console.log(MyFunc());
var array = ["1","2","3","4","5"];
var current = "0";
function MyFunc()
{
console.log("Iteration");
for(var i = 0; i < array.length; i++)
{
current += array[i];
console.log(current);
}
};
console.log(MyFunc());
console.log(MyFunc());
Variables are named memory, it all depend on the scope of the variable(Local or Global) Variables inside a functions are called local variables, every time this function is called the variables are re initialized, which means at every call to the function the value of the variable is expected to change.
public void myFunction(int val1, String val2)
{
int a = val1;
String b = val2;
System.out.println(val2 +" "+val1);
}
a and b in the above are local variable that will be affected base on the parameters passed to it by the method/function calls
so the value of a variables in a function will always change
I have a for loop where I get the start number for my countdown. I want to use that number outside the for loop and here comes the closure problem.
for (var i = 0; i < response.length; i++) {
var number = response[i].number; //the number is 10
getNumber(number);
};
So I thought I should call a function that returns that number so I can use it somewhere else:
function getNumber(number) {
return number;
}
But when I try to do this, I get an undefined instead of 10:
var globalVariableForNumber = getNumber();
What I know I am doing wrong is calling getNumber() without the parameter when assigning the value to my variable, but how else should I do it?
The number comes from an ajax call that has more numbers in it (response[i].number). I then want to use those numbers to be the start timers of my countdown. So if the number is 10, then my countdown will start from 10.
Thank you.
var response = [
{number:5},
{number:6},
{number:7},
{number:8},
{number:9},
{number:10}
]
var number;
for (var i = 0; i < response.length; i++) {
number = response[i].number; //the number is 10
console.log(getNumber());
};
function getNumber() {
return number;
}
// try it again later...
console.log(getNumber());
Again, you should just be calling number directly. But for the purpose of this question you have to declare number in a higher scope.
Scope
The current context of execution. The context in which values and
expressions are "visible," or can be referenced. If a variable or
other expression is not "in the current scope," then it is unavailable
for use. Scopes can also be layered in a hierarchy, so that child
scopes have access to parent scopes, but not vice versa.
A function serves as a closure in JavaScript, and thus creates a
scope, so that (for example) a variable defined exclusively within the
function cannot be accessed from outside the function or within other
functions.
Consider this example of scopes that your code could be declared and called:
function ImInGlobalScope(){ //Function declared in global scope
//Lets call this block 1
for (var i = 0; i < response.length; i++) {
var number = response[i].number; //Inside function scope
getNumber(number)
};
//Lets call this block 2
function getNumber(number){ //In same scope than for statement
return number; //block 3
}
}
var globalVariableForNumber = getNumber();
With this example getNumber is undefined because it belongs to ImInGlobalScope() scope. Consider another scenario of scopes for your code.
function ImInGlobalScope(){ //Function declared in global scope
//Lets call this block 1
for (var i = 0; i < response.length; i++) {
var number = response[i].number; //Inside function scope
getNumber(number)
};
}
//Lets call this block 2
function getNumber(number){ //In same scope than for statement
return number; //block 3
}
var globalVariableForNumber = getNumber();
I believe the above is what your scenario is: getNumber is on global scope but number is ImInGlobalScope().
So when you're calling var globalVariableForNumber = getNumber(); We have the following:
function getNumber(number){ //Number is not being passed so is undefined
return number; //no variable named number exists in this scope so it will return undefined.
}
What you are doing is creating a function that takes exactly one parameter and returns that parameter when it is called. Return is a fancy way of saying that if you say foo=bar() then foo is whatever bar() returned.
In your code, calling getNumber() with no parameter returns undefined because it just returns the parameter. What you should do instead is not return the parameter, but instead set the global variable to it like so:
function getNumber(number) {
globalVariableForNumber = number;
}
Now, just get the number by running:
globalVariableForNumber
I have the following code:
for(var i = 0; i < nodelist.length; i++) {
var x = functionThatCreatesADivElement();
someElement.appendChild(x.getDiv()); // this works fine
nodelist[i].onclick = function() {
x.someFunction(); // this always refer to the last 'x' object
}
}
function functionThatCreatesADivElement() {
var div = document.createElement("div");
this.someFunction = function() {}
this.getDiv = function() {
return div;
}
return this;
}
the problem is that the execution of nodelist[0].onclick is exactly the same as nodelist[4].onclick (assuming that i = 4 is the last node).
I believe the references of the previously iterated are changing to point to the currently iterated element.
What is the proper way of doing this?
EDIT: Added some more code and changed the name of the function cause it was too confusing
You have two problems. The first problem is that JavaScript variables don't have block scopes.
From MDN:
When you declare a variable outside of any function, it is called a global variable, because it is available to any other code in the current document. When you declare a variable
within a function, it is called a local variable, because it is available only within that
function.
JavaScript does not have block statement scope;
You aren't enclosing a the x variable in a function, so all of your onclick callbacks are using the same x variable, which point to whatever element is last in the loop since that will be the last one to have overwritten x.
Doing this for your loop should work:
nodelist.forEach(function (nodeitem) {
var x = functionThatCreatesADivElement();
someElement.appendChild(x.getDiv());
nodeitem.onclick = function() {
x.someFunction();
}
});
The second problem is that your functionThatCreatesADivElement() constructor function is not being called correctly. Use new functionThatCreatesADivElement() since you are invoking a constructor function.
Solved. I had to use
var x = new functionThatCreatesADivElement();
function functionThatCreatesADivElement() {
var div = document.createElement("div");
this.someFunction = function() {}
this.getDiv = function() {
return div;
}
//return this; //Using new instead of returning this
}
I'm trying to create a function which returns another function. I want separate information when each of the inner function is run, but this isn't happening. I know that explanation is not great, so I've put together a small example.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return that;
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
This outputs 2, 2. What I would like is 2, 4 to be output. I know this isn't explained perfectly, so if it's not clear what I'm trying to achieve, can someone explain why the variable seems to be shared across the two functions?
Thanks
Like this ?
var testFn = function(testVal) {
var test = testVal
return {
getVal: function() {
return test
}
}
};
var ab = testFn (4)
var ac = testFn (2)
console.log(ab.getVal(),ac.getVal()) //4 //2
The problem in your code is this.getVal() / returning this
because 'this' refers to the global scope / Window
You are glubbering with the global namespace and overwriting Window.getVal() , the moment you are setting b = testFn (2)
This results in overwriting as method getVal too because they both refer to the global Object and always share the same method getVal
Therefore they share the same closure and are outputing 2
console.log("The same: " + (Window.a === Window.b)) // true
console.log("The same: " + (a === b)) // true
you can see that if you change it a little:
var testFn = function(testVal) {
var x = {}
return (function(testVal) {
var test = testVal;
x.getVal = function () {
return test;
}
return x
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());//4 2
it suddenly works because it results in 2 different Objects returned (btw you don't even need the outer closure)
console.log("The same: " + (a === b)) // false
Here are the JSbins First / Second
I hope you understand this, I'm not good in explaining things
If theres anything left unclear, post a comment and I'll try to update the answer
This question comes down to the context in which functions are invoked in JavaScript.
A function that is invoked within another function is executed in the context of the global scope.
In your example, where you have this code:
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
})(testVal);
}
The inner function is being called on the global scope, so this refers to the global object. In JavaScript a function executed within another function is done so with its scope set to the global scope, not the scope of the function it exists within. This tends to trip developers up a fair bit (or at least, it does me!).
For argument's sake, lets presume this is in a browser, so hence this refers to the window object. This is why you get 2 logged twice, because the second time this runs, this.getVal overwrites the getVal method that was defined when you ran var a = testFn(4);.
JavaScript scopes at function level, so every function has its own scope:
var x = 3;
function foo() {
var x = 2;
console.log(x);
};
console.log(x); //gives us 3
foo(); // logs 2
So what you want to do is run that inner function in the context of the testFn function, not in the global scope. You can run a function with a specific context using the call method. I also recorded a screencast on call and apply which discusses this in greater detail. The basic usage of call is:
function foo() {...}.call(this);
That executes foo in the context of this. So, the first step is to make sure your inner function is called in the right context, the context of the testFn method.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
}.call(this, testVal);
}
The first parameter to call is the context, and any arguments following that are passed to the function as parameters. So now the inner function is being called in the right scope, it wont add getVal to the global scope, which is a step in the right direction :)
Next though you also need to make sure that every time you call testFn, you do so in a new scope, so you're not overwriting this.getVal when you call testFn for the second time. You can do this using the new keyword. This SO post on the new keyword is well worth reading. When you do var foo = new testFn() you create and execute a new instance of testFN, hereby creating a new scope. This SO question is also relevant.
All you now need to do is change your declaration of a and b to:
var a = new testFn(4);
var b = new testFn(2);
And now console.log(b.getVal(), a.getVal()); will give 2, 4 as desired.
I put a working example on JSBin which should help clear things up. Note how this example defines this.x globally and within the function, and see which ones get logged. Have a play with this and hopefully it might be of use.
The output you get is (2,2) because when you do
var that = this;
what you actually get is the global object (window),
the object that holds all the global methods and variables in your javascript code.
(Note that every variable that is not nested under an object or function is global and
every function that is not nested under an object is global, meaning that functions that are nested under a function are still global)
so, when you set:
var test = testVal;
this.getVal = function() {
return test;
}
you actually set the function "getVal" in the global object, and in the next run you will again set the same function - overriding the first.
To achieve the affect you wanted I would suggest creating and object and returning it in the inner function (as #Glutamat suggested before me):
var testFn = function(testVal) {
return new Object({
getVal: function() {
return testVal;
}
});
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
In this way, in the outer function we create an object with an inner function called "getVal" that returns the variable passed to the outer function (testVal).
Here's a JSBin if you want to play around with it
(thanks to #Glutamat for introducing this site, I never heard of it and it's really cool :D)
If I declare a function literal:
var x = function(){
alert('hi');
};
console.log(x); // returns the function code.
However:
var x = (function(){
alert('hi');
})();
console.log(x); // returns undefined?
I don't understand why this happens. Isn't the point of writing a function as a literal is to still be able to access it by its variable reference name? I know this may be silly but I'm just learning javascript so don't judge too harshly.
Your function does not return anything, so its return value is undefined.
A self-executing function is executed and the function is not stored anywhere - only its return value survives (and any external variables the function sets/modifies).
For example, this code would be equivalent to var x = 'hi';:
var x = (function(){
return 'hi';
})();
The purpose of self-invoking functions is usually to create a new scope, e.g. when creating callback functions in a loop:
for(var i = 0; i < 5; i++) {
window.setTimeout(function(){ alert('i = ' + i); }, 1000 * i);
}
This would use the same i in all callbacks so it would alert i = 5 5 times.
for(var i = 0; i < 5; i++) {
(function(i) {
window.setTimeout(function(){ alert('i = ' + i); }, 1000 * i);
})(i);
}
By using a self-executing function we create a new scope and thus a new i in each loop.
Another use of self-executing functions is to create a new scope where certain variables are ensured to be available and set to the correct value:
(function($, window, undefined) {
// here the following always applies:
// $ === jQuery
// window === the global object [assuming the function was executed in the global scope]
// undefined is well, undefined - in some js engines someone could have redefined it
})(jQuery, this);
If you:
var foo = somefunction;
… then you assign a function to foo.
If you:
var foo = somefunction();
… then you assign the return value of a function call to foo
Your function:
function(){
alert('hi');
}
… has no return statement, so it will return undefined.