Calling context and lexical scoping - javascript

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.

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.

Does this function have closure over this block?

I am experimenting with closures but rather than enclosing a function within a function, I enclosed a function within a block. Since functions are not blocked-scoped and will be hoisted outside of the block I assumed that it wouldn't have access to the scope within the block. Yet, in this case, the function returns the block-scoped variable. Does this mean that the function is a closure?
{
let a = 'hi'
function test() {
return a
}
}
test() // hi
I'd be happy to call it a closure, at least according to Wikpedia's definition:
Operationally, a closure is a record storing a function together with an environment. The environment is a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created. Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.
In your function test, the variable a is a free variable, used by the function but not defined inside the function. When you called the function outside the block, it retained the value of a. So, you've met the essential points of the definition of closure (according to Wikipedia).
Of course, you asked the question because it's kind of tricky. Normally with closures, we define a function inside an environment and then "export" it out by binding the function object to a name that has a wider scope. Because of the way JavaScript treats function declarations defined in a block (see Code Maniac's link to the ECMAScript specification on handling block-scoped function declarations) you do get this effect! So it's kind of a closure, even though you never explicitly exported the function.
Of course if you had written
{
let a = 'hi'
let test = () => a
}
test()
you get an error.
But this works:
let b;
{
let a = 'hi'
let test = () => a
b = test
}
b() // "hi"
so yes, the block is acting as the non-local environment from which variables can be captured. I'm thinking yes, it's okay to speak of this as being a closure, because it acts like one (even if this behavior comes from a pre-ECMAScript 2015, "optional", and non-strict treatment of function declarations inside of blocks). If it walks like a duck, and all that.

Do I have to use `var` when declaring a function in javascript?

Consider the following 2 functions:
cat = function() {
console.log("Meow");
}
and:
var dog = function() {
console.log("woof")
}
cat() -> "Meow"
dog() -> "Woof
They both work, except that cat is not declared using var. Does this mean that it is globally scoped? I would say that both functions are globally scoped. There is also the syntaxfunction cat(){...}, I guess that is similar to the first style, some sort of implicit variable binding...
Could somebody explain the difference between the styles for declaring functions, if there is any.
Variables
If you don't specify var, let or const it will get globally scoped
If you do specify var, let or const then it will get scoped to the nearest enclosing scope depending on that particular specifier
(var - will get scoped to the nearest enclosing function or global scope if not defined inside of a function)
(let & const - will get scoped to the nearest enclosing block)
Functions
Assigning a function as follows:
var dog = function() {
console.log("woof")
}
Means that the function will not be accessible until the line that it is declared on is reached during execution, i.e. you will only be able to execute this function from after the line on which it was declared.
Whereas declaring a function as follows:
function cat(){...}
Means that it will be moved to the top of the enclosing scope, so you will be able to call it from anywhere within the reachable scope even if it's earlier in code than the line on which you declared it on.
Not using var/let/const makes it implicitly global, which is generally regarded as a bad thing. If you use 'use strict', you'll get an error for any implicit globals. The biggest issue that arises with implicit global variables is that you may not know that you've made a global variable. For example:
(function() {
a = 5;
})();
// a doesn't exist right?
console.log(a); // 5... whoops!
No you don't. you can declare a function like so:
function foo(){
}
Then foo is automatically declared at the appropriate scope.
Its all a matter of scopes. where will the interpreter declare the function. Doing it the way you did it, without var will cause the interpreter to create a global variable automatically, and that is the highest scope possible, meaning that it is accessible everywhere in the code. That is considered a bad thing, since you normally wouldn't want to do that unless it is done intentionally, because of deep reasons which I can go into if you wish.
function foo(){
function bar(){
console.log("bar");
}
bar();
console.log(typeof bar);
}
foo();
bar(); // will throw an error since "bar" does not exist at this scope
Read all about function declaration

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.

Why is this console log affected by a subsequent if statement?

Consider the following code:
var a = 'a';
function b() {
console.log(a);
if (!a) {
var a = 'b';
}
}
b();
Running b() prints undefined to the console. However, if you remove the if statement, or even simply remove the var keyword from the expression within the if statement so that you're redefining the outer a variable, the string a will be printed to the console as expected.
Can anyone explain this? The only cause I can think of is that this is a race condition, and the if statement is running just a tad faster than the console.log.
This is not a race condition - it's a language feature and is working as the designers of the Javascript language intended.
Because of hoisted variable definitions (where all variable definitions within a function scope are hoisted to the top of the function), your code is equivalent to this:
var a = 'a';
function b() {
var a;
console.log(a);
if (!a) {
a = 'b';
}
}
b();
So, the locally declared a hides the globally declared a and initially has a value of undefined until your if statement gives it a value.
You can find lots of discussion about this characteristic of the Javascript language by searching for "Javascript hoisting".
When you use var statement in a function, it will be creating a new variable which is local to that function.
All the variables declared in the function, will be moved to the top of the function, irrespective of the place where they are actually declared. This is called Hoisting.
The hoisted variables will have the value undefined, by default, till they are explicitly assigned a value.
It prints undefined as it is, because of the 3rd point.
In your case, you declared a variable a within the if block. Since the variables are hoisted, the declaration is moved to the top of the function. It has the same name as the outer variable. When you access a in the function, it first looks in the current scope if there is a variable by that name exists. It checks other scopes only if it is not found in local scope. So, the local a shadows the outer a. When you remove the var statement, there is no a in local scope, so the outer a is used. That is why it prints a.

Categories

Resources