Weird behavior of if(true) on JavaScript scope..? - javascript

So I was testing scope of JavsScript (var, let, and const), and I found a weird behavior that I couldn't really understand.
var write = document.getElementById('demo');
function test() {
var m = 'hello';
}
test();
write.innerHTML = m;
//This doesn't work
In above case, JS cannot accesses var m declared locally from global scope.
var write = document.getElementById('demo');
if(true) {
var m = 'hello';
}
write.innerHTML = m;
//This works
In above case, JS accesses var m declared locally from global scope.
Can someone explain why that is..?

It is because of Hoisting. Also var has function level scope
var write = document.getElementById('demo');
var m; // m will be undefined here, declared but not initialized
if (true) {
m = 'hello'; // m is initialized her
}
write.innerHTML = m;
<div id='demo'></div>

In the first case, var declaration is scoped at function level and thus its limited to function test
In the second case, if scope is block scope and hence var declaration escapes into the global scope due to Hoisting
Thus in the first case you can't use m outside function test but you can do so in the second case.

Related

Are there any contexts / situations in ES6 Modules where it wouldn't work to update var to let?

For some time now, I've used const to declare most variables.
If I don't absolutely know for certain that a new variable will always hold the same value, I will declare the new variable with let.
I almost never use var.
That's me.
However, any other individual writing javascript will have their own preferred variable declaration conventions and, if I am accepting user-generated-content such as User Written ES6 Modules there's no way predict with confidence what those conventions might be.
Leaving aside const for the moment...
var can be overwritten by var:
I know that the following:
var myString = 'My String declared with var';
var myFunction = function() {console.log('My Function declared with var')};
can be overwritten with identically named variables declared with var.
Example:
var myString = 'My String declared with var';
var myFunction = function() {console.log('My Function declared with var')};
var myString = 'My Overwriting String declared with var';
var myFunction = function() {console.log('My Overwriting Function declared with var')};
console.log(myString);
myFunction();
let cannot be overwritten by var:
I also know that if myString and myFunction are declared with let
let myString = 'My String declared with let';
let myFunction = function() {console.log('My Function declared with let')};
they cannot subsequently be overwritten with var, let, or const.
Example:
let myString = 'My String declared with let';
let myFunction = function() {console.log('My Function declared with let')};
var myString = 'My Overwriting String declared with var';
var myFunction = function() {console.log('My Overwriting Function declared with var')};
console.log(myString);
myFunction();
So, knowing both these things, is it a reasonable safeguard to convert any var declarations to let declarations (either at compile time or at runtime) in all User Submitted ES6 Modules?
Are there any conceivable contexts / situations in ES6 Modules where it wouldn't work to update var to let?
If a user wrote a function like this, changing var to let would fail:
function run() {
for (var i = 0; i < 3; i++) {
console.log(i);
}
console.log('final i', i);
}
console.log('starting run');
run();
function runLet() {
for (let i = 0; i < 3; i++) {
console.log(i);
}
console.log('final i', i);
}
console.log('starting runLet');
runLet();
the runLet function errors because i is undefined.

variabiles accessibiity when a function launch an other function - javascript

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
}

JS hoisting functions. ( Why does this code snippet work? )

The following code snippet shows a basic object that contains a variable str, a member variable hello, and a member function test. I thought I knew JS pretty well and expected this code to fail because the test function would be hoisted to the top and would be unable to access the str or vm variable. I was then surprised to see that this actually works. Why does this code function? Does hoisting still occur?
function SomeObject( ) {
var vm = this;
vm.hello = "hello";
vm.test = test;
var str = "testig!";
function test() {
console.log( vm.hello );
console.log( str );
}
}
var s = new SomeObject();
s.test();
Output:
hello
testig!
Due to hoisting, you essentially end up with this:
function SomeObject() {
var vm;
var str;
var test;
test = function test() {
console.log(vm.hello);
console.log(str); // Works because we haven't run the function yet
}
vm = this;
vm.hello = 'hello';
vm.test = test;
str = 'testig'; // str now has a value, function hasn't been called yet
}
var s = new SomeObject();
s.test(); // Function is called after str has been given a value
All declarations are hoisted to the top of their enclosing scope container.
A function declaration, such as:
function foo(){ }
Will be hoisted to the top of its enclosing scope, so it may be called by code that is written BEFORE the function is.
Variable declarations are hoisted as well. So, code such as:
var x = 10;
Will experience hoisting as well. BUT, only declarations are hoisted, so in the previous example, only
var x
is hoisted. The x = 10 assignment won't happen until the actual code location is reached.
Similarly, function expressions work the same way. With this code:
var f = function() {};
Only the var f is hoisted, not the assignment. If you were to try to call f before the actual code location were reached, you would receive an error indicating that f is not a function.
Your code works simply because when you call:
var s = new SomeObject();
The function test is not executed, but all the variable assignments are. So, when the time comes for this:
s.test();
All the variables and the function are ready to go.

Declaring two variable with the same name

Is it possible to call the same name variables which set outside of the function?
var a = $(window).width(); // I want to call this variable
if(!$.isFunction(p)){
var a = $(window).height(); // Not this one
alert(a);
}
FIDDLE
In this case, you have actually redefined the value of a. There is absolutely no way of referencing a different variable with the same name, as it just acts as a redefinition.
If you want to declare a global variable you can do so by
window.varname="This is a global variable";
And you can access the same by
alert(window.varname);
Now you can also have a local variable inside a function with the same name
var varname="This is a local variable";
And you can access it normally.
Here's your code so that you can access the global variable not the local one.
var p = ['foo',''];
window.a = $(window).width();
if(!$.isFunction(p)){
var a = $(window).height();
alert(window.a);
}
In a code snippet such as yours, the variable a is being redefined. This is because an if statement doesn't create another scope for variables. However, functions do.
In a case like this:
var a = 0; // global
function doStuff() {
var a = 10; // local
alert(a);
alert(window.a)
}
alert(a);
doStuff();
alert(a);
inside the function doStuff, the variable a is being redefined. This snipped will therefore alert the numbers 0, 10, 0, 0. This proves that the global variable is not redefined inside the function, as printing a after calling doStuff doesn't change the value of a in the global scope.
The variable a outside of the function can be accessed, as any variable not declared in a non-global scope is placed inside the window object. However, if using this snippet (which calls an anonymous function, creating a new scope):
var a = 0; // global
function doStuff() {
var a = 10; // local
alert(a);
alert(window.a)
function() {
var a = 20; // even more local
alert(a);
alert(window.a);
}();
}
alert(a);
doStuff();
alert(a);
you cannot access the value of a inside the doStuff function. You can still access the global variable using window.a.
In your case, however, the if statement does not create a new scope, therefore you are redefining the variable a to the new value $(window).height().
Example:
var a=10;
if(true){
var a=5;
}
alert(a)// it will return a=5;
var a=10;
var a=5;
//both are same way assign value
In js if statement is not scope it visible every where with in function . you have to change the variable name
There is no blockscope in JavaScript (at least up until ES6).
Like you seem to expect from the if block. See
What is the scope of variables in JavaScript?
for an excellent summary of scopes that do exist in JavaScript.
Beware of Hoisting
Furthermore, you shouldn't sprinkle your var declarations through your code, but explicitly put them in the top of your function. That is where Javscript will hoist them anyway:
# so if you have a function like this
var i = 5;
function testvar () {
alert(i);
var i=3;
}
testvar();
# the alert window will contain undefined.
# because internally, it's been changed into this:
var i = 5;
function testvar () {
var i;
alert(i);
i=3;
}
testvar();
Minimize use of the global scope
Read
What is meant by “leaking” into global scope?
And listen to what Doug Crockford has to say about it. Actually, take an hour and watch the whole talk.
You can do it like this:
var p = ['foo',''];
var a = $(window).width(); // I want to call this variable
if(!$.isFunction(p)){
(function(b){
var a = $(window).height();
alert(b);
})(a);
}
No need to use the global scope, just create an anonymous function and call it with a as the argument. Inside the function b is a reference to the a variable outside the function.
It is a good practice not to modify the window object in javascript to write clean and maintainable code.
Less bugs and problems. I mean, never do the window.a thing. Is evil for your code.
No, you can't because of you have redefined the variable name in the same scope and beacuse of the hoisted variables your code will be interpreted by javascript in the following mode:
var p, a;
p = ['foo',''];
a = $(window).width(); // I want to call this variable
if(!$.isFunction(p)){
a = $(window).height(); // Not this one
alert(a);
}
Now you can easly see that the variable a will be replaced and not created
JavaScript has two scopes: global and local. In your example a is in the global scope both times so you are just redefining it.
However you can specify skip a variable in local scope and get the one from global. Consider this example:
var a = 1;
function foo () {
var a = 2;
console.log("var a is: " + window.a);
console.log("local var a is: " + a);
}
foo ();
Will log "var a is: 1"\n"local var a is: 2\n" to the console. This is about as close as it gets to what you need
var abc = new Array();
abc[0] = 'str1';
abc[1] = 'str2';
Use array in this case
Try this (pattern)
var p = ['foo', ''];
var a = function (name) {
return (
name === "height"
? $(window).height()
: (name === "width" ? $(window).width() : name)
)
};
if (!$.isFunction(p)) {
// `$(window).width()` , `$(window).height()`
alert( a("width") + "\n" + a("height") );
}
jsfiddle http://jsfiddle.net/guest271314/2tuK4/

Why is the variable inside this function global?

I thought any variable defined in a function would be local but I can easily access variable 'e' outside of its function.
function change() {
var d = 6;
e = 7;
}
change();
alert(e); //> alerts 7
Because new variables will enter the global scope by default. var prevents this from happening by constraining a variable's existence to be within the current scope.
Because it was declared without var it becomes part of the global window object.
You've not explicitly declared it as such, so it has taken global scope.
Thats because e is global by default, using var make a scope varible.
You can read more about this in Javascript Garden Scope and Namespaces
I am guessing that you are going under this assumption that
JSLint expects that a var will be
declared only once, and that it will
be declared before it is used.
Problem with your code is you are using one var, but your second line has no var in front of it. That is pushing that varaible e into the global namespace.
Why is it happening? You used a semicolon instead of a comma in the variable declaration.
function change() {
var d = 6, //Change this to a comma
e = 7;
}
change();
alert(e); //will produce an error now
It is surprisingly easy to create global variables, here are some other gotchas I've seen.
// :-( antipattern: implied global variable
function sum(x, y) {
result = x + y; // result is global
return result;
}
// :-) better
function sum(x, y) {
var result = x + y; // result is local
return result;
}
// :-( antipattern: chain assignments as part of a var declaration
function foo() {
var a = b = 0; // b is global
}
// :-) better
function foo() {
var a, b;
a = b = 0; // both local
}

Categories