Can variables declared in different local scopes be accessed? - javascript

I've been learning the javascript syntax for about a week now.
How can I move variables from one local scope to another local scope? So for example i've created a for loop off screen that adds an array of values to one main array every round of the loop, the main array is called x. I've not included the loop as it's irrelevant to the issue. In the example below, the purpose for variable y off screen is essentially to take the values contained within the arrays of x to retrieve more data via a web scrape.
Context aside, I can't access the y variable declared inside the if statement. As you can see below i'm trying to use it as an argument in a function called functionName.
if (x.length === 1){
let y = x[0]
} else {
let y = x[0][2]
};
functionName(y)
Is it better practices to duplicate the function call for every outcome of the if statement like this
if (x.length === 1){
let y = x[0]
functionName(y)
} else {
let y = x[0][2]
functionName(y)
};
Or can different local scopes actually communicate with each other.
It definitely feels less practical to call every function for every outcome of the if statement, especially if it's a larger conditional switch statement.
Thanks in advance.

How can I move variables from one local scope to another local scope?
You can't. That's the point of scopes.
Is it better practices to duplicate the function call for every outcome of the if statement like this
No. Put the variable in a shared scope.
{
let y;
if (x.length === 1){
y = x[0]
} else {
y = x[0][2]
};
functionName(y)
}

You can't "move" the scope of a variable dynamically. A variable's scope is completely determined by the block (or function) it's declared in. For two blocks to be able to reference the same variable, that variable must be declared outside of both blocks, in an ancestor block:
let y;
if (x.length === 1){
y = x[0]
} else {
y = x[0][2]
};
functionName(y)
But, in this case, since you want to conditionally assign to y, you should use the conditional operator:
const y = x.length === 1
? x[0]
: x[0][2];
functionName(y)

In Javascript, now, there are two types of scope: Block Scope and Function Scope. First there was light, ehm sorry that was another stackexchange site, first there was only function scope.
With the introduction of this new scope also introduced was the different way of declaring a variable, let, also const, both of which defines a block scoped variable. Before there was only var which defined and still defines a function scoped variable.
Block Scope is inside the if, for and similar blocks which usually enclosed lexically by a pair of curly braces {}, not to be confused by the objects, which have same enclosing as well.
Function scope is inside the functions, of all type whether fat arrow or normal ones, hence the name.
So you should either define the y with let, const or var once and outside of the if blocks you did. Once you the definition outside of the block you can use any of them since block scope is no longer your problem, or alternatively, define it in the first if block if you must with var so it is still in the scope outside of the if block it is defiend in then do use it in your assignment in else as y = something since it in the scope in there too. Though first way is better.
let y;
if (x.length === 1){
y = x[0]
functionName(y) // or put call outside the block
} else {
y = x[0][2]
functionName(y) // or put call outside the block
};
// functionName(y); // or put call outside the block here
Also note in your first example the let y in the else block would create a different variable than the one defined in the preceding if since let, also const, defines a new block scoped variable.
Finally, variables cannot be moved from one scope to another or be accessed from outside of the scope they defined in per se, but you can use the scopes to your advantage like defining a variable you need in some of the scopes you have access to, or opposite if you need to enclose it to a scope. It may also be possible to create some custom constructs to share scopes between blocks or functions, I can imagine closures, however this may be a bit out of scope of the post now.

Related

Does top level functions makes closure in JavaScript?

I am learning closure and have basic doubt.
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
let counter = makeCounter();
Here when we are returning function it has closure with outer scope. so it has a closure with count.
var x = 1;
function func() {
console.log(x); // does func has closure with x ??
}
func();
So my question is , only nested function makes closure ??
or top level func has also closure with x ??
Closures in JavaScript are implemented using the [[OuterEnv]] internal field of the [[EnvironmentRecord]] internal field of function declarations. The [[OuterEnv]] value is then copied to the execution context created any time a function is called. This value is then used to create the scope chain, enabling the closure behavior.
Functions declared in the global context are no different in this regard and maintain a link to their surrounding environment, the Global Environment Record.
In the global context, variables declared using var and function declarations behave subtly differently to those declared inside other functions, in that they end up as properties on the global object, but this is an implementation detail.
For the sake of conversation, lets say that every closure creates a "scope" where variables are defined - everything within the closure/scope can access variables, anything outside cannot. With that said, there are two different types of implied closures you should be aware of.
This is not an exhastive description of how closures work, but more of description of the basic practical concepts as they relate to this question. There is much more going on with how call stacks are created, lexical scoping, "global" scope within NodeJS, iframes, sandboxing, var vs const/let, block vs function scopes, and other concepts which I will leave to the reader to discover on their own.
The browser/window/global scope
In the browser, you have the top-level scope (basically window, often referred to as the "global" scope). This kind of acts as a closure in that all "non-enclosed" variables are defined as part of the window scope and are available to most other code running on that page. For example, if you include the following script in the browser, x is part of the global/window scope, whereas y and z are enclosed inside their own nested scopes.
// basic-script.js
// x becomes part of the global/window scope
const x = 10;
(() => {
// y is enclosed in a separate, nested closure
const y = 20;
// x is available in this nested scope/closure
console.log('The sum of x and y is', x + y);
function foo() {
// z is enclosed in a separate, nested closure
const z = 5;
// x and y are both available in this nested scope/closure
console.log('The sum of x y and z is', x + y + z);
}
foo();
// z is not available to the parent scope
console.log(z); //-> undefined
})();
// y is not available to the parent scope
console.log(y); //-> undefined
<script src="basic-script.js"></script>
<script>
// this works because x is not enclosed
console.log('The value of x is', x);
// y and z are not available as they are inside a separate closure
console.log(y, z); //-> undefined, undefined
</script>
JavaScript modules
When you write JavaScript which is imported or required by other modules, every module is automatically wrapped in its own closure. For example, if you were to import/require the above basic-script.js into another JavaScript file, the above would be automatically enclosed like this:
(function() {
var x = 10;
// ... other code removed for brevity
})();
There is some other magic going on for exposing exports and so forth, but that's beyond the scope of this answer (pun intended). If JS modules were not wrapped in their own closure, then you would have a mess of naming collisions and reference issues, as well as a security nightmare.
So to answer your question, yes - your second example shares a closure with "x" - the closure is implicitly created depending on your environment.
Don't know, if you are still searching for the answer or not.
What is you meant by top level, I think you are talking about the global scope. To understand closure one should understand scope better.
And No, Closure in not work in global scope. The main reason of closure is to create a function that return a function with specific variables/calculations with it so that you can reuse that value again. I have a detailed blog in scope, closure and lexical environment. You can check it out https://shimulhsn.hashnode.dev/lexical-environment-and-scope-chain
A practical use of closures is creating a "scope" of protection around variables and functions declared within that function from outside interference. The top scope is "global", accessed using the window variable (an object). Everything, regardless what scope it was declared in, has access to it.
A function declared in global scope creates its own scope that no expression in the global scope and other "sub-global" scopes has access to. Functions declared in the global scope or declared inside other functions create their own scope, that even its immediate parent scope doesn't have access to.
Any variable declared—var, let, const—inside a function block is accessible in that function scope and any sub-scope created within that function scope—i.e., nothing outside that function scope can access it, (except a mechanism the function creating the scope may provide).
The second example in the question, where x is declared in the global scope, means everything has access to x. If x is later changed it is changed in all scopes. x is wide-open to interference anywhere in the code.
var x = 1; <-- declared outside func()
function func() {
// ...etc., etc., etc.
In the first example:
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
...makeCounter() creates a scope that includes count and an anonymous function, and that has access to them. Sub-scopes within this scope also have access to them. However, any expression in the global scope or other sub-global scopes do not have access to them.
Regarding the description that closures give access to an outer function's scope from an inner function—that scope access travels "up", accessing every parent scope, all the way to global scope, regardless how far "down" the "scope-chain" it goes. For example:
const can_i_reach_global = 'yes';
topLevel();
function topLevel () {
console.log('topLevel func, can_i_reach_global:', can_i_reach_global);
oneDeep();
function oneDeep () {
console.log('oneDeep func, can_i_reach_global:', can_i_reach_global);
twoDeep();
function twoDeep () {
console.log('twoDeep func, can_i_reach_global:', can_i_reach_global)
}
}
}
So, as far as practical definitions go, simply stating function closure "...gives you access to an outer function's scope from an inner function..." doesn't describe the usefulness of this programming construct.

Calling context and lexical scoping

Consider the following code.I’m not sure I completely understand lexical scoping, but, unless I’m misinterpreting everything I’ve been reading, an inner function can only reference outer variables that have been declared in the same context.
let a = 7;
function test() {
return a;
}
function test2() {
let a = 5;
return test()
}
console.log(test2())
In other words, what matters is where such functions are born and NOT where they are called, which, in the case of my example code above, means that the variable “a” that test returns is not the same as the variable “a” declared and assigned the value of 5 in test2. It’s as if the “a” in test2 were foreign to test.
Am I on the right track or is there something I’m missing?
You seem to be on the right track.
Variables defined with let are local to whatever scope they are defined in, either the global scope or a function or a block inside a function. Code in a function or block can "see" variables declared within the same function or block, or variables in an outer scope such as an enclosing function or the global scope.
Your code has two distinct variables that happen to share the same name, a. Each let statement creates a brand new variable, at whatever scope the let statement appears in.
Because your two a variables are actually different and unrelated variables, we could rename them to have two different names, and the code will work the same. I will use global_a for one of them and test2_a for the other:
let global_a = 7;
function test() {
return global_a;
}
function test2() {
let test2_a = 5;
return test()
}
console.log(test2())
Now when you look at the code, you can see that test() only uses global_a. It doesn't even try to use test2_a. And in the test2() function, you can see that the test2_a variable is only assigned but never used!
Note that variables defined with var follow basically the same rules, except that inner blocks inside a function (like an if statement with curly braces) don't count. var only follows function scope and ignores blocks inside a function.

How variable can be accessible outside the for loop

In the code below, I have declared variable i on for loop , and trying to access variable i and I can get the last updated value of i, how.
for(var i = 0; i <= 10; i++){
console.log(i);
}
console.log(i,'outside');
The var statement declares a variable in the scope of the current function, not the current block (which is what the let statement is for).
After the for loop, you are still in the same function, so the variable still exists (with whatever value it was last set to).
(NB: For the purposes of var, the code which runs outside of any function is effectively treated as being in a function of its own).
Well, you are using var, and that's exactly a var type should do!
We have var and let, let's see how they work.
when you declare your variable like this :
let something;
You only access something in that very scope (for example your own for loop), and outside the scope, you cannot have it. On the other hand, when you are using var type, like this :
var something;
You are able to use this variable outside the scope ( again like your own for loop )
Notice that we are not talking about global variables. we are just talking about scopes.
If you rather declare a global variable, just use var in the global scope.

Access variable in if block

var func = function () {
var i: number = 0;
if (i == 0) {
var y: number = 1;
}
document.body.innerHTML = y.toString(); // js/ts should complain me in this line
};
func(); // output: 1
As you can see, I've declared variable y inside if block. So, I think it couldn't be referenced outside the scope.
But, when I've tried to run the code, the output is 1.
Is it an issue in typescript/javascript?
Variables in Javascript are hoisted, meaning that var y is moved to the top of the function as if it were declared there.
Initialization, however is not hoisted, so if you change i to be something other than 0, the variable y will still exist, but it will have the value undefined.
This means that the function is exactly equivalent to:
var func = function () {
var i: number = 0;
var y: number;
if (i == 0) {
y = 1;
}
document.body.innerHTML = y.toString(); // js/ts should complain me in this line
};
To get the behavior you expect, you need to use let, which is a part of ECMAScript 2015 (ES6). It is block scoped, as you expect. It will also effectively work so that it is accessible only from the point of definition onwards, which is also probably as you would expect.
If you re-declare a JavaScript variable, it will not lose its value.
The second reference might pave way for a new variable syntax. Actually if you recall variable declaration is not neccessary in javascript. Simpe
y=1;
also works.
The second time when you reference y, outside if block, in my opinion, it tries a re-declaration and retains the old value.
Reference - http://www.w3schools.com/js/js_variables.asp
& http://www.w3schools.com/js/js_scope.asp
Javascript has function scope afaik. Any variable declared within a function, should be accessible from anywhere within the function. So, if you have a function checking if i==0, then you can achieve what you are trying to achieve.
This is as it is supposed to be. Javascript scopes by function, not by block. (This does make it unlike many popular languages)
Variables declared like var myVar = 10; will seep into nested functions but not the other way around. Variables declared like myVar = 10; will go global.
I couldn't find anything which suggested that typescript was any different.
Variables declared inside of an if statement are not scoped to the if statement. They're scoped to the current execution context. There's the Global execution context and then when a function is run, it creates it's own execution context. Inside of your function, you created the variables y and i. It doesn't matter that y was created inside of the if statement, because once it runs, y is created in the scope of the function. So then you do y.toString(), which can access y because it's scoped to the function not the if statement. That's why you get the output of 1. This is not an error, it's by design.

About scoping in JavaScript

I need to have some information about the scoping in JavaScript. I know that it supports lexical (static) scoping, but, does not it support dynamic scoping as well?
I think you're confused because Javascript uses static scoping but at function-level, not at block level like usual structured languages.
var foo = "old";
if (true) {var foo = "new";}
alert (foo == "new")
So be careful, blocks don't make scope!
That's why you sometimes see loops with functions inside just to enable variables whose scope is inside an iteration:
functions = [];
for(var i=0; i<10; i++) {
(function(){
var local_i = i;
functions[local_i] = function() {return local_i;}
})();
}
functions[2]() // returns 2 and not 10
As far as I understood; Javascript has two kinds of variables which are global and local variables. But, suppose we have a variable called x, which is defined as global, and defined in the static parent of the scope of place where x is referenced. In this case, x takes the value of the global variable. Thus, global variable has higher priority than local ones. And, when there is no any global variables, x finds the declaration through the static chain which makes me think that Javascirpt is staticaly scoped language.
Am I right at above?

Categories

Resources