calValue: function (data) {
var self = this;
var values = data.values;
for (var i = 0; i < data.length; i++) {
if(data.condition == 0){
(function (values) {
for (i = 0; i < values.length; i++) {
}
})(values)
}
else{
//do sth else
}
}
}
my understanding is each funtion has its own context and the variable being declared inside it, will only be effetive inside. Like above code snippet, i expect the "i" variable inside the inner for-loop won't impact the outer one "i" variable. However, the fact is it do affect.
Could someone please help explain? Thanks.
With var declarations, scope is at the function level. Such declarations are interpreted as if they appeared at the very start of the enclosing function.
In modern JavaScript environments, the let declaration allows you to create variables scoped to local blocks of statements. (Also const for non-modifiable symbols.)
In your case, that inner i in the nested function just refers to that i declared externally. From inside a function, you can always "see" out, but you can't "see" in. That's how scope works.
Related
This has always been a lingering question for me: If block scopes are created when a let or const identifier is enclosed within curly brackets, then how is the let identifier in the initialization statement of a for loop not available in the enclosing scope but is instead available inside the curly brackets of the for loop?
(function() {
for (let i = 0; i < 5; i++) {
console.log(i) // logs current value of i
}
console.log(i) // referenceError
})()
That's just how things work. A variable declared at the top of a for loop like that is only visible inside the current iteration's for block. You can think of it a bit like this:
<loop> {
let i = getCount();
console.log(i) // logs current value of i
}
where getCount runs the logic that increments i.
Variables declared with let are block-scoped - it wouldn't make sense for i to be referenceable outside. If the i was visible outside of the for, what would you expect its value to be? You already have a separate i binding for every iteration of the loop. It wouldn't make sense to somewhat-arbitrarily pick one of those bindings to be visible outside.
Because it's a local variable in the for loop and not outside it. If you had declared it outside the loop, you could access it correctly.
(function() {
let i;
for (i = 0; i < 5; i++) {
console.log(i);
}
console.log(i);
})();
If you don't want to change your syntax, you could use var.
(function() {
for (var i = 0; i < 5; i++) {
console.log(i);
}
console.log(i);
// this should work.
})()
Otherwise, a variable declared with let or const has what's called Block Scope meaning that it's only visible inside the curly braces it was defined in. That's essentially the fundamental difference between let and var. A variable declared with var has Function Scope (It's visible throughout the function, even before it's lexical definition, it's value will just be undefined)
(function() {
console.log(i); // undefined
var i = 10;
console.log(i); // 10
})();
But the example above will only work if there is no "use strict" anywhere.
i'm writing a script to manage HTML menu interactions with clicks from user so this my situation:
menuScripts('site-header-branding', 'menu-mobile-toggle', 'site-header-navigation', 'main-navigation', 'sub-menu');
function menuScripts(siteHeaderBrandingCSSClass, hamburgerCSSClass, wrapperOfMainNavigationCSSClass, ulMainNavigationCSSClass, subMenuCSSClass) {
var classeSiteHeaderBranding = siteHeaderBrandingCSSClass; //site-header-branding
var classeHamburger = hamburgerCSSClass; //menu-mobile-toggle
var classeWrapperOfMainNavigation = wrapperOfMainNavigationCSSClass;//site-header-navigation
var classeMainNavigation = ulMainNavigationCSSClass; //main-navigation
var classeUlSubMenus = subMenuCSSClass;//sub-menu
const siteHeaderBrandingDOM = document.getElementsByClassName(classeSiteHeaderBranding);
for (let i = 0; i< siteHeaderBrandingDOM.length; i++) {
siteHeaderBrandingDOM[i].addEventListener("click", HeaderBrandingInteractive);
};
const menu = document.getElementsByClassName(classeMainNavigation);
for (let i = 0; i< menu.length; i++) {
menu[i].addEventListener("click", SubMenuInteractive);
};
}
function HeaderBrandingInteractive(e) {
//magic
}
function SubMenuInteractive(e) {
//magic
}
And it give me an error , because inside the last two function i need to have access to some of the variables declared in menuScripts(){}
These variables don't exist inside the last two function.
But if i remove "var" from declaration, so like this
menuScripts('site-header-branding', 'menu-mobile-toggle', 'site-header-navigation', 'main-navigation', 'sub-menu');
function menuScripts(siteHeaderBrandingCSSClass, hamburgerCSSClass, wrapperOfMainNavigationCSSClass, ulMainNavigationCSSClass, subMenuCSSClass) {
classeSiteHeaderBranding = siteHeaderBrandingCSSClass; //site-header-branding
classeHamburger = hamburgerCSSClass; //menu-mobile-toggle
classeWrapperOfMainNavigation = wrapperOfMainNavigationCSSClass;//site-header-navigation
classeMainNavigation = ulMainNavigationCSSClass; //main-navigation
classeUlSubMenus = subMenuCSSClass;//sub-menu
const siteHeaderBrandingDOM = document.getElementsByClassName(classeSiteHeaderBranding);
for (let i = 0; i< siteHeaderBrandingDOM.length; i++) {
siteHeaderBrandingDOM[i].addEventListener("click", HeaderBrandingInteractive);
};
const menu = document.getElementsByClassName(classeMainNavigation);
for (let i = 0; i< menu.length; i++) {
menu[i].addEventListener("click", SubMenuInteractive);
};
}
function HeaderBrandingInteractive(e) {
//magic
}
function SubMenuInteractive(e) {
//magic
}
It works!
i tried also to pust "const" instead of "var", but same problem of accessibility.
In theory
var x = 'something';
should must be equal to
x = 'something' ;
What i didnt get from the theory of javascript??
The variables declared with var are scoped to the enclosing function.
When you do x = 'something', the variable x will be globally created at the time of assignment - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var.
Assigning a value to an undeclared variable implicitly creates it as a
global variable (it becomes a property of the global object) when the
assignment is executed.
In your code, you are first assigning variables before making the function call to HeaderBrandingInteractive-
classeSiteHeaderBranding = siteHeaderBrandingCSSClass; //site-header-branding
classeHamburger = hamburgerCSSClass; //menu-mobile-toggle
classeWrapperOfMainNavigation = wrapperOfMainNavigationCSSClass;//site-header-navigation
classeMainNavigation = ulMainNavigationCSSClass; //main-navigation
classeUlSubMenus = subMenuCSSClass;//sub-menu
The above code will create global variables, like window.classeHamburger. So, it will be accessible to outside your function.
var x = 'something'; should NOT be equal to x = 'something';, because if you declare variable as var x = 'something'; in the scope of function, this variable is "visible" in the scope of particular function.
When you put simply x = 'something';, that variable becomes decrared in the global scope and will be accessible as window.x.
It is Not Recommended to declare a variable without var keyword. It can accidentally overwrite an existing global variable.
Scope of the variables declared without var keyword become global irrespective of where it is declared. Global variables can be accessed from anywhere in the web page. Visit Scope for more information.
Javascript use three kinds of variable declaration.
var: The scope of a variable declared with var is its current execution context, which is either the enclosing function or, for variables declared outside any function, global.
let: let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword locally to an entire function regardless of block scope.
const: Constants are block-scoped, much like variables defined using the let statement. The value of a constant cannot change through reassignment, and it can't be re-declared.
Neither of the above: If you do not specify var or let then JS considers that variable as global which means it can be accessed throughout your script.
Note: The variable declarations are hoisted, which means the declarations will be pushed to the top of the code (Within that scope for let/const, within that function for var and to the top of the whole script for global) but the initialization will not. Learn More...
In your above code when you are using var the variables are only limited to the scope of menuScripts.
When you do not specify anything, the variables become global and hence can be accessed anywhere.
Thanks for all explications you provided.
Now it's more clear to me the var local/global concept.
Since it's worthwhile to use always "var" keyword to protect future edit of the variable from outside - thanks #G.M. Patel - i had the problem to be able to pass arguments to a function launched via .addEventListener .
I looked at How to pass arguments to addEventListener listener function? but those solutions didn't help me.
What i did is to shift up the two functions (HeaderBrandingInteractive and SubMenuInteractive) so they are parts of menuScript .
In this way the local variables of menuScript are accessible from HeaderBrandingInteractive and SubMenuInteractive without writing code.
Now it works.
Maybe will help somebody in the future.
menuScripts('site-header-branding', 'menu-mobile-toggle', 'site-header-navigation', 'main-navigation', 'sub-menu');
function menuScripts(siteHeaderBrandingCSSClass, hamburgerCSSClass, wrapperOfMainNavigationCSSClass, ulMainNavigationCSSClass, subMenuCSSClass) {
var classeSiteHeaderBranding = siteHeaderBrandingCSSClass; //site-header-branding
var classeHamburger = hamburgerCSSClass; //menu-mobile-toggle
var classeWrapperOfMainNavigation = wrapperOfMainNavigationCSSClass;//site-header-navigation
var classeMainNavigation = ulMainNavigationCSSClass; //main-navigation
var classeUlSubMenus = subMenuCSSClass;//sub-menu
const siteHeaderBrandingDOM = document.getElementsByClassName(classeSiteHeaderBranding);
for (let i = 0; i< siteHeaderBrandingDOM.length; i++) {
siteHeaderBrandingDOM[i].addEventListener("click", HeaderBrandingInteractive);
};
const menu = document.getElementsByClassName(classeMainNavigation);
for (let i = 0; i< menu.length; i++) {
menu[i].addEventListener("click", SubMenuInteractive);
};
function HeaderBrandingInteractive(e) {
//magic
}
function SubMenuInteractive(e) {
//magic
}
}
I've also tried to delete
var classeSiteHeaderBranding = siteHeaderBrandingCSSClass; //site-header-branding
var classeHamburger = hamburgerCSSClass; //menu-mobile-toggle
var classeWrapperOfMainNavigation = wrapperOfMainNavigationCSSClass;//site-header-navigation
var classeMainNavigation = ulMainNavigationCSSClass; //main-navigation
var classeUlSubMenus = subMenuCSSClass;//sub-menu
cause #James said that it's a waste of time, and it's correct.
But you need to change forward use of those variables so they match the new name.
For example my SubMenuInteractive was :
function SubMenuInteractive(e) {
//some code
if (e.toElement.parentElement.parentElement.getAttribute('class') == classeMainNavigation ) {
console.log("it's him");
}
//some other code
}
and now need to be like this, with "classeMainNavigatin" that become "ulMainNavigationCSSClass" like in the "menuScript" declaration:
function SubMenuInteractive(e) {
//some code
if (e.toElement.parentElement.parentElement.getAttribute('class') == ulMainNavigationCSSClass ) {
console.log("it's him");
}
//some other code
}
This is my code:
function func(){
for(i=0; i < 5; i++){
alert('g');
}
}
for(i=0; i < 5; i++){
func();
alert('h');
}
What I expected was: gggghgggghgggghgggghggggh
but what received was just ggggh
I found out that's because there is function scope, not block scope in JS. What I'd like to know is how to preserve such a behavior. I mean to force something like block scope. Otherwise it's very easy to make really nasty bugs - e.g. while using a function somebody else wrote or the one you wrote yourself, but a few months earlier.
This actually doesn't have to do with function vs. block scope - it has to do with implicit global variables.
The short story: You are accidentally creating a global variable in your for loop - if you use for(var i=0; rather than for(i=0; you'll get the expected results.
The slightly longer version:
alert("typeof i: " + typeof i);
// Alerts "typeof i: undefined"
function func() {
// References the *global* variable `i`
for(i=0; i < 5; i++){
alert('g');
}
}
// Creates a *global* variable `i` and sets it to 0
for(i=0; i < 5; i++) {
alert("i at start of iteration: " + i);
// Alerts "i at start of iteration: 0"
func();
// `func` has just altered *global* state - here's the proof
alert("i after call to func: " + i);
// Alerts "i at start of iteration: 5"
alert('h');
}
alert("typeof i: " + typeof i);
// Alerts "typeof i: number
// `i` is now in the global scope.
// Left as an exercise for the reader:
// try it again with `var i=0;` in both of the `for` loops.
Issue with variable i's scope.
function func(){
for(var i=0; i < 5; i++){
alert('g');
}
}
for(var i=0; i < 5; i++){
func();
alert('h');
}
Try declaring i as a local variable in your function:
function func(){
var i;
for(i=0; i < 5; i++){
alert('g');
}
}
As already stated in other answers, your code doesn't work because you don't declare the i variable with var, which automatically makes it global. So not really a function versus block scope issue. If you use var to declare your variable(s) then the scope of the variable(s) will be limited to the same function where the declaration is including within any nested functions, or global if not in a function.
If you use the same variable name in nested functions then the one in the inner scope is said to "shadow" the outer one; the inner function can't access the outer function's variable of that name in that case since using that name will only give it its own variable. So to speak. Though you can access global variables by treating them as properties of the window object.
"What I'd like to know is how to preserve such a behavior. I mean to force something like block scope. Otherwise it's very easy to make really nasty bugs..."
Well no, once you start using var correctly it isn't difficult to avoid those bugs. But you can force block scope by introducing an immediately invoked anonymous function:
(function() {
// variables created in this function are accessible
// only within this function
var x, y, z;
})();
console.log(typeof x); // "undefined" (x can't be seen from outside the function)
The parentheses around function(){} are required so that the function is interpreted as an expression and not a function statement; the extra () at the end is to cause that function expression to be executed immediately. A common use for this structure is to enclose an entire script in it so that no variables or functions in it become global and thus don't interact with other scripts loaded from other JS include files. (You can have pseudo-global variables within the block shared between several functions also declared within the block.)
So for example if you wanted to make the body of a for statement into a "block" with scope you could do this:
for (var i = 0; i < something; i++) {
(function() {
var x = i;
// do something with x
})();
}
I'm reading this article
and there is a paragraph:
If you ever find yourself needing to explicitly scope a variable
inside a function you can use an anonymous function to do this. You
can actually create an anonymous function and then execute it straight
away and all the variables inside will be scoped to the anonymous
function:
(function() {
var myProperty = "hello world";
alert(myProperty);
})();
alert(typeof(myProperty)); // undefined
I met with this already but still need some clarification why should I need to explicitly scope a variable inside a function when variables are implicitly scoped inside a function in Javascript.
Could you explain the purpose of this?
thank you
for (var i = 0; i < 10; i++) {
setTimeout(function() { console.log(i) }, 10);
}
// alerts 10, 10 times
for (var i = 0; i < 10; i++) {
(function(i) {
// explicitly scope i
setTimout(function() { console.log(i) }, 10);
})(i);
}
When generating functions inside other functions and accessing variables up the scope chain through closure scope it may be useful to "explicitly scope" a variable inside the outer function.
Although this is an anti pattern. The correct solution would be
var generateLogger = function(i) {
return function() { console.log(i); };
}
for (var i = 0; i < 10; i++) {
setTimeout(generateLogger(i), 10);
}
Since generating functions in a loop is inefficient and bad practice.
There are no real use cases of "explicitly scoping" variables that can't be avoided by not creating functions inside other functions.
Here's some JavaScript:
linkElem.click(function () {
var data = linkElem.data();
alert(''+data.mls + ' ' + data.id);
});
It works.
linkElem is a local variable that I create in a loop inside a function. I assign some data to it with jQuery's .data(). If I did not call .click(), linkElem would be reassigned during the loop and then recycled after the function returns. However, I have created an anonymous function which references linkElem. So I am no longer sure what is going on.
My guess is that all of the anonymous functions and linkElems created during the loop are given UIDs of some kind and moved to persistent/global scope. Is this correct? Gratuitous detail would be much appreciated.
Yes, your description is pretty close. The local storage for a Javascript function call is just a block of memory allocated for local variables. If you "capture" that by creating another function inside a called function, then the storage is retained and the local variables carry on with their lives, unaware that the function that gave them birth might be long dead.
It's important to keep in mind that only functions create such storage — things like brace-enclosed loop bodies are not separate storage areas. Thus a common error is to declare a variable in a function and re-use it among several functions created in a loop. That's not inherently wrong, but the effect can be surprising:
function whatever() {
for (var i = 0; i < 3; ++i) {
setTimeout(function() { alert(i); }, 5000);
}
}
If you run that, you'll see three alerts that all say "3". Why? Because they all share the same "i" variable. You can avoid that by introducing another function layer:
function whatever() {
for (var i = 0; i < 3; ++i) {
setTimeout((function(private_i) { return function() { alert(private_i); }; })(i), 5000);
}
}
The "wrapper" function is just there to provide a local variable (the parameter "private_i") whereto the loop variable "i" can be copied.
However, I have created an anonymous function which references linkElem. So I am no longer sure what is going on.
It still gets reassigned, unless you are wrapping it in another level of scope (NB: another function).
Consider the following:
for (var j = 0;j < 10;j += 1) {
arrayOfLinks[j].onclick = function () {
alert(j);
};
}
In this case, all of those links would alert 10 when clicked, because j is outside of the scope and is being updated.
If you're creating linkElem in the same way, you are likely to only get the result of the last linkElem in the loop.
This is a better way:
linkElem.click(function () {
var data = $(this).data(); // no longer dependent on `linkElem` reference
alert(''+data.mls + ' ' + data.id);
});
Please refer to this How do JavaScript closures work?
This may help you understanding closures.
Whenever you see the function keyword within another function, the inner function has access to variables in the outer function.
function foo(x) {
var tmp = 3;
function bar(y) {
alert(x + y + (++tmp));
}
bar(10);
}
foo(2)
This will always alert 16, because bar can access the x which was defined as an argument to foo, and it can also access tmp from foo.
That is a closure. A function doesn't have to return in order to be called a closure. Simply accessing variables outside of your immediate lexical scope creates a closure.
function foo(x) {
var tmp = 3;
return function (y) {
alert(x + y + (++tmp));
}
}
var bar = foo(2); // bar is now a closure.
bar(10);
The above function will also alert 16, because bar can still refer to x and tmp, even though it is no longer directly inside the scope.
However, since tmp is still hanging around inside bar's closure, it is also being incremented. It will be incremented each time you call bar.
The simplest example of a closure is this:
var a = 10;
function test() {
console.log(a); // will output 10
console.log(b); // will output 6
}
var b = 6;
test();
When a Javascript function is invoked, a new execution context is created. Together with the function arguments and the parent object, this execution context also receives all the variables declared outside of it (in the above example, both 'a' and 'b').
It is possible to create more than one closure function, either by returning a list of them or by setting them to global variables. All of these will refer to the same x and the same tmp, they don't make their own copies.
[you]: Fascinating, tell me more!
Here the number x is a literal number. As with other literals in JavaScript, when foo is called, the number x is copied into foo as its argument x.
On the other hand, JavaScript always uses references when dealing with Objects. If say, you called foo with an Object, the closure it returns will reference that original Object!
function foo(x) {
var tmp = 3;
return function (y) {
alert(x + y + tmp);
x.memb = x.memb ? x.memb + 1 : 1;
alert(x.memb);
}
}
var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);
As expected, each call to bar(10) will increment x.memb. What might not be expected, is that x is simply referring to the same object as the age variable! After a couple of calls to bar, age.memb will be 2! This referencing is the basis for memory leaks with HTML objects.