What is the scope of variables in JavaScript?

What is the scope of variables in javascript? Do they have the same scope inside as opposed to outside a function? Or does it even matter? Also, where are the variables stored if they are defined globally?

Answers:

Answer

TLDR

JavaScript has lexical (also called static) scoping and closures. This means you can tell the scope of an identifier by looking at the source code.

The four scopes are:

  1. Global - visible by everything
  2. Function - visible within a function (and its sub-functions and blocks)
  3. Block - visible within a block (and its sub-blocks)
  4. Module - visible within a module

Outside of the special cases of global and module scope, variables are declared using var (function scope), let (block scope) and const (block scope). Most other forms of identifier declaration have block scope in strict mode.

Overview

Scope is the region of the codebase over which an identifier is valid.

A lexical environment is a mapping between identifier names and the values associated with them.

Scope is formed of a linked nesting of lexical environments, with each level in the nesting corresponding to a lexical environment of an ancestor execution context.

These linked lexical environments form a scope "chain". Identifier resolution is the process of searching along this chain for a matching identifier.

Identifier resolution only occurs in one direction: outwards. In this way, outer lexical environments cannot "see" into inner lexical environments.

There are three pertinent factors in deciding the scope of an identifier in JavaScript:

  1. How an identifier was declared
  2. Where an identifier was declared
  3. Whether you are in strict mode or non-strict mode

Some of the ways identifiers can be declared:

  1. var, let and const
  2. Function parameters
  3. Catch block parameter
  4. Function declarations
  5. Named function expressions
  6. Implicit variable declarations
  7. import statements
  8. eval

Some of the locations identifiers can be declared:

  1. Global context
  2. Function body
  3. Ordinary block
  4. The top of a control structure (eg. loop, if, while etc)
  5. Control structure body
  6. Modules

Declaration Styles

var

Identifiers declared using var have function scope, apart from when they are declared directly in the global context, in which case they are added as properties on the global object and have global scope. There are separate rules for their use in eval functions.

let and const

Identifiers declared using let and const have block scope, apart from when they are declared directly in the global context, in which case they have global scope.

Note: let, const and var are all hoisted. This means that their logical position of definition is the top of their enclosing scope (block or function). However, variables declared useing let and const cannot be read or assigned to until control has passed the point of declaration in the source code. The interim period is known as the temporal dead zone.

function f() {
    function g() {
        console.log(x)
    }
    let x = 1
    g()
}
f() // 1 because x is hoisted even though declared with `let`!

Function parameter names

Function parameter names are scoped to the function body.

Function declarations

Function declarations have block scope in strict mode and function scope in non-strict mode. Note: non-strict mode is a complicated set of emergent rules based on the quirky historical implementations of different browsers.

Named function expressions

Named function expressions are scoped to themselves (eg. for the purpose of recursion).

Implicit variable declarations

In non-strict mode, implicit variable declarations have global scope. In strict mode these are not permitted.

eval

In eval strings, variables declared using var will be placed in the current scope, or, if eval is used indirectly, as properties on the global object.

Examples

The following will throw a ReferenceError because the namesx, y and z have no meaning outside of the function f.

function f() {
    var x = 1
    let y = 1
    const z = 1
}
console.log(typeof x) // undefined (because var has function scope!)
console.log(typeof y) // undefined (because the body of the function is a block)
console.log(typeof z) // undefined (because the body of the function is a block)

The following will throw a ReferenceError for y and z, but not for x, because the visibility of x is not constrained by the block. Blocks that define the bodies of control structures like if, for and while, behave similarly.

{
    var x = 1
    let y = 1
    const z = 1
}
console.log(x) // 1
console.log(typeof y) // undefined because `y` has block scope
console.log(typeof z) // undefined because `z` has block scope

In the following, x is visible outside of the loop because var has function scope:

for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)

...because of this behavior you need to be careful about closing over variables declared using var in loops. There is only one instance of variable x declared here, and it sits logically outside of the loop.

The following prints 5, five times, and then prints 5 for an sixth time for the console.log outside the loop:

for(var x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
}
console.log(x) // note: visible outside the loop

The following prints undefined because x is block-scoped. The callbacks are run one by one asynchronously. New behavior for let variables means that each anonymous function closed over a different variable named x (unlike it would have done with var), and so integers 0 through 4 are printed.:

for(let x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
}
console.log(typeof x) // undefined

The following will NOT throw a ReferenceError because the visibility of x is not constrained by the block; it will, however, print undefined because the variable has not been initialised (because of the if statement).

if(false) {
    var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised

A variable declared at the top of a for loop using let is scoped to the body of the loop:

for(let x = 0; x < 10; ++x) {} 
console.log(typeof x) // undefined, because `x` is block-scoped

The following will throw a ReferenceError because the visibility of x is constrained by the block:

if(false) {
    let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped

Variables declared using var, let or const are all scoped to modules:

// module1.js

var x = 0
export function f() {}

//module2.js

import f from 'module1.js'

console.log(x) // throws ReferenceError

The following will declare a property on the global object, because variables declared using var within the global context, are added as properties to the global object:

var x = 1
console.log(window.hasOwnProperty('x')) // true

let and const in the global context do not add properties to the global object, but still have global scope:

let x = 1
console.log(window.hasOwnProperty('x')) // false

Function parameters can be considered to be declared in the function body:

function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function

Catch block parameters are scoped to the catch-block body:

try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block

Named function expressions are scooped only to the expression itself:

(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression

In non-strict mode, implicit variable declarations are globally scoped. In strict mode you get an error.

x = 1 // implicit variable declaration (no "var"!)

console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true

In non-strict mode, function declarations have function scope. In strict mode they have block scope.

'use strict'
{
    function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped

How it works under the hood

Scope is defined as the lexical region of code over which an identifier is valid.

In JavaScript, every function-object has a hidden [[Environment]] reference that is a reference to the lexical environment of the execution context (stack frame) within which it was created.

When you invoke a function, the hidden [[Call]] method is called. This method creates a new execution context and establishes a link between the new execution context and the lexical environment of the function-object. It does this by copying the [[Environment]] value on the function-object, into an outer reference field on the lexical environment of the new execution context.

Note that this link between the new execution context and the lexical environment of the function object is called a closure.

Thus, in JavaScript, scope is implemented via lexical environments linked together in a "chain" by outer references. This chain of lexical environments is called the scope chain, and identifier resolution occurs by searching up the chain for a matching identifier.

Find out more.

Answer

Javascript uses scope chains to establish the scope for a given function. There is typically one global scope, and each function defined has its own nested scope. Any function defined within another function has a local scope which is linked to the outer function. It's always the position in the source that defines the scope.

An element in the scope chain is basically a Map with a pointer to its parent scope.

When resolving a variable, javascript starts at the innermost scope and searches outwards.

Answer

Variables declared globally have a global scope. Variables declared within a function are scoped to that function, and shadow global variables of the same name.

(I'm sure there are many subtleties that real JavaScript programmers will be able to point out in other answers. In particular I came across this page about what exactly this means at any time. Hopefully this more introductory link is enough to get you started though.)

Answer

Old school JavaScript

Traditionally, JavaScript really only has two types of scope :

  1. Global Scope : Variables are known throughout the application, from the start of the application (*)
  2. Functional Scope : Variables are known within the function they are declared in, from the start of the function (*)

I will not elaborate on this, since there are already many other answers explaining the difference.


Modern JavaScript

The most recent JavaScript specs now also allow a third scope :

  1. Block Scope : Identifiers are "known" from the top of the scope they are declared within, but they cannot be assigned to or dereferenced (read) until after the line of their declaration. This interim period is called the "temporal dead zone."

How do I create block scope variables?

Traditionally, you create your variables like this :

var myVariable = "Some text";

Block scope variables are created like this :

let myVariable = "Some text";

So what is the difference between functional scope and block scope?

To understand the difference between functional scope and block scope, consider the following code :

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

Here, we can see that our variable j is only known in the first for loop, but not before and after. Yet, our variable i is known in the entire function.

Also, consider that block scoped variables are not known before they are declared because they are not hoisted. You're also not allowed to redeclare the same block scoped variable within the same block. This makes block scoped variables less error prone than globally or functionally scoped variables, which are hoisted and which do not produce any errors in case of multiple declarations.


Is it safe to use block scope variables today?

Whether or not it is safe to use today, depends on your environment :

  • If you're writing server-side JavaScript code (Node.js), you can safely use the let statement.

  • If you're writing client-side JavaScript code and use a browser based transpiler (like Traceur or babel-standalone), you can safely use the let statement, however your code is likely to be anything but optimal with respect to performance.

  • If you're writing client-side JavaScript code and use a Node based transpiler (like the traceur shell script or Babel), you can safely use the let statement. And because your browser will only know about the transpiled code, performance drawbacks should be limited.

  • If you're writing client-side JavaScript code and don't use a transpiler, you need to consider browser support.

    These are some browsers that don't support let at all :

    • Internet explorer 10 and below
    • Firefox 43 and below
    • Safari 9 and below
    • Android browser 4 and below
    • Opera 27 and below
    • Chome 40 and below
    • ANY version of Opera Mini & Blackberry Browser

enter image description here


How to keep track of browser support

For an up-to-date overview of which browsers support the let statement at the time of your reading this answer, see this Can I Use page.


(*) Globally and functionally scoped variables can be initialized and used before they are declared because JavaScript variables are hoisted. This means that declarations are always much to the top of the scope.

Answer

Here's an example:

<script>

var globalVariable = 7; //==window.globalVariable

function aGlobal( param ) { //==window.aGlobal(); 
                            //param is only accessible in this function
  var scopedToFunction = {
    //can't be accessed outside of this function

    nested : 3 //accessible by: scopedToFunction.nested
  };

  anotherGlobal = {
    //global because there's no `var`
  }; 

}

</script>

You'll want to investigate closures, and how to use them to make private members.

Answer

The key, as I understand it, is that Javascript has function level scoping vs the more common C block scoping.

Here is a good article on the subject.

Answer

In "Javascript 1.7" (Mozilla's extension to Javascript) one can also declare block-scope variables with let statement:

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4
Answer

The idea of scoping in JavaScript when originally designed by Brendan Eich came from the HyperCard scripting language HyperTalk.

In this language, the displays were done similar to a stack of index cards. There was a master card referred to as the background. It was transparent and can be seen as the bottom card. Any content on this base card was shared with cards placed on top of it. Each card placed on top had its own content which took precedence over the previous card, but still had access to the prior cards if desired.

This is exactly how the JavaScript scoping system is designed. It just has different names. The cards in JavaScript are known as Execution ContextsECMA. Each one of these contexts contains three main parts. A variable environment, a lexical environment, and a this binding. Going back to the cards reference, the lexical environment contains all of the content from prior cards lower in the stack. The current context is at the top of the stack and any content declared there will be stored in the variable environment. The variable environment will take precedence in the case of naming collisions.

The this binding will point to the containing object. Sometimes scopes or execution contexts change without the containing object changing, such as in a declared function where the containing object may be window or a constructor function.

These execution contexts are created any time control is transferred. Control is transferred when code begins to execute, and this is primarily done from function execution.

So that is the technical explanation. In practice, it is important to remember that in JavaScript

  • Scopes are technically "Execution Contexts"
  • Contexts form a stack of environments where variables are stored
  • The top of the stack takes precedence (the bottom being the global context)
  • Each function creates an execution context (but not always a new this binding)

Applying this to one of the previous examples (5. "Closure") on this page, it is possible to follow the stack of execution contexts. In this example there are three contexts in the stack. They are defined by the outer context, the context in the immediately invoked function called by var six, and the context in the returned function inside of var six's immediately invoked function.

i) The outer context. It has a variable environment of a = 1
ii) The IIFE context, it has a lexical environment of a = 1, but a variable environment of a = 6 which takes precedence in the stack
iii) The returned function context, it has a lexical environment of a = 6 and that is the value referenced in the alert when called.

enter image description here

Answer

1) There is a global scope, a function scope, and the with and catch scopes. There is no 'block' level scope in general for variable's -- the with and the catch statements add names to their blocks.

2) Scopes are nested by functions all the way to the global scope.

3) Properties are resolved by going through the prototype chain. The with statement brings object property names into the lexical scope defined by the with block.

EDIT: ECMAAScript 6 (Harmony) is spec'ed to support let, and I know chrome allows a 'harmony' flag, so perhaps it does support it..

Let would be a support for block level scoping, but you have to use the keyword to make it happen.

EDIT: Based on Benjamin's pointing out of the with and catch statements in the comments, I've edited the post, and added more. Both the with and the catch statements introduce variables into their respective blocks, and that is a block scope. These variables are aliased to the properties of the objects passed into them.

 //chrome (v8)

 var a = { 'test1':'test1val' }
 test1   // error not defined
 with (a) { var test1 = 'replaced' }
 test1   // undefined
 a       // a.test1 = 'replaced'

EDIT: Clarifying example:

test1 is scoped to the with block, but is aliased to a.test1. 'Var test1' creates a new variable test1 in the upper lexical context (function, or global), unless it is a property of a -- which it is.

Yikes! Be careful using 'with' -- just like var is a noop if the variable is already defined in the function, it is also a noop with respect to names imported from the object! A little heads up on the name already being defined would make this much safer. I personally will never use with because of this.

Answer

I found that many people new to JavaScript have trouble understanding that inheritance is available by default in the language and that function scope is the only scope, so far. I provided an extension to a beautifier I wrote at the end of last year called JSPretty. The feature colors function scope in the code and always associates a color to all variables declared in that scope. Closure is visually demonstrated when a variable with a color from one scope is used in a different scope.

Try the feature at:

See a demo at:

View the code at:

Currently the feature offers support for a depth of 16 nested functions, but currently does not color global variables.

Answer

JavaScript have only two type of scope :

  1. Global Scope : Global is nothing but a window level scope.Here, variable present throughout the application.
  2. Functional Scope : Variable declared within a function with var keyword has functional scope.

Whenever a function is called, a variable scope object is created (and included in scope chain) which is followed by variables in JavaScript.

        a = "global";
         function outer(){ 
              b = "local";
              console.log(a+b); //"globallocal"
         }
outer();

Scope chain -->

  1. Window level - a and outer function are at top level in scope chain.
  2. when outer function called a new variable scope object(and included in scope chain) added with variable b inside it.

Now when a variable a required it first searches for nearest variable scope and if variable is not there than it move's to next object of variable scope chain.which is in this case is window level.

Answer

Just to add to the other answers, scope is a look-up list of all the declared identifiers (variables), and enforces a strict set of rules as to how these are accessible to currently executing code. This look-up may be for the purposes of assigning to the variable, which is an LHS (lefthand-side) reference, or it may be for the purposes of retrieving its value, which is an RHS (righthand-side) reference. These look-ups are what the JavaScript engine is doing internally when it's compiling and executing the code.

So from this perspective, I think that a picture would help that I found in the Scopes and Closures ebook by Kyle Simpson:

image

Quoting from his ebook:

The building represents our program’s nested scope ruleset. The first floor of the building represents your currently executing scope, wherever you are. The top level of the building is the global scope. You resolve LHS and RHS references by looking on your current floor, and if you don’t find it, taking the elevator to the next floor, looking there, then the next, and so on. Once you get to the top floor (the global scope), you either find what you’re looking for, or you don’t. But you have to stop regardless.

One thing of note that is worth mentioning, "Scope look-up stops once it finds the first match".

This idea of "scope levels" explains why "this" can be changed with a newly created scope, if it's being looked up in a nested function. Here is a link that goes into all these details, Everything you wanted to know about javascript scope

Answer

run the code. hope this will give an idea about scoping

Name = 'global data';
document.Name = 'current document data';
(function(window,document){
var Name = 'local data';
var myObj = {
    Name: 'object data',
    f: function(){
        alert(this.Name);
    }
};

myObj.newFun = function(){
    alert(this.Name);
}

function testFun(){
    alert("Window Scope : " + window.Name + 
          "\nLocal Scope : " + Name + 
          "\nObject Scope : " + this.Name + 
          "\nCurrent document Scope : " + document.Name
         );
}


testFun.call(myObj);
})(window,document);
Answer

Global Scope :

Global variables are exactly like global stars (Jackie Chan, Nelson Mandela). You can access them (get or set the value), from any part of your application. Global functions are like global events (New Year, Christmas). You can execute (call) them from any part of your application.

//global variable
var a = 2;

//global function
function b(){
   console.log(a);  //access global variable
}

Local Scope :

If you are in the USA, you may know Kim Kardashian, infamous celebrity ( she somehow manages to make the tabloids). But people outside of the USA will not recognize her. She is a local star, bound to her territory.

Local variables are like local stars. You can only access them (get or set the value) inside the scope. A local function is like local events - you can execute only (celebrate) inside that scope. If you want to access them from outside of the scope, you will get a reference error

function b(){
   var d = 21; //local variable
   console.log(d);

   function dog(){  console.log(a); }
     dog(); //execute local function
}

 console.log(d); //ReferenceError: dddddd is not defined    

Check this article for in-depth understanding of scope

Answer

There are ALMOST only two types of JavaScript scopes:

  • the scope of each var declaration is associated with the most immediately enclosing function
  • if there is no enclosing function for a var declaration, it is global scope

So, any blocks other than functions do not create a new scope. That explains why for-loops overwrite outer scoped variables:

var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5

Using functions instead:

var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10

In the first example, there was no block scope, so the initially declared variables were overwritten. In the second example, there was a new scope due to the function, so the initially declared variables were SHADOWED, and not overwritten.

That's almost all you need to know in terms of JavaScript scoping, except:

So you can see JavaScript scoping is actually extremely simple, albeit not always intuitive. A few things to be aware of:

  • var declarations are hoisted to the top of the scope. This means no matter where the var declaration happens, to the compiler it is as if the var itself happens at the top
  • multiple var declarations within the same scope are combined

So this code:

var i = 1;
function abc() {
  i = 2;
  var i = 3;
}
console.log(i);     // outputs 1

is equivalent to:

var i = 1;
function abc() {
  var i;     // var declaration moved to the top of the scope
  i = 2;
  i = 3;     // the assignment stays where it is
}
console.log(i);

This may seem counter intuitive, but it makes sense from the perspective of a imperative language designer.

Answer

Modern Js, ES6+, 'const' and 'let'

You should be using block scoping for every variable you create, just like most other major languages. var is obsolete. This makes your code safer and more maintainable.

const should be used for 95% of cases. It makes it so the variable reference can't change. Array, object, and DOM node properties can change and should likely be const.

let should be be used for any variable expecting to be reassigned. This includes within a for loop. If you ever change value beyond initialization, use let.

Block scope means that the variable will only be available within the brackets in which it is declared. This extends to internal scopes, including anonymous functions created within your scope.

Answer

There are only function scopes in JS. Not block scopes! You can see what is hoisting too.

var global_variable = "global_variable";
var hoisting_variable = "global_hoist";

// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);

if (true) {
    // The variable block will be global, on true condition.
    var block = "block";
}
console.log("global_scope: - block: " + block);

function local_function() {
    var local_variable = "local_variable";
    console.log("local_scope: - local_variable: " + local_variable);
    console.log("local_scope: - global_variable: " + global_variable);
    console.log("local_scope: - block: " + block);
    // The hoisting_variable is undefined at the moment.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);

    var hoisting_variable = "local_hoist";
    // The hoisting_variable is now set as a local one.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}

local_function();

// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);
Answer

Try this curious example. In the example below if a were a numeric initialized at 0, you'd see 0 and then 1. Except a is an object and javascript will pass f1 a pointer of a rather than a copy of it. The result is that you get the same alert both times.

var a = new Date();
function f1(b)
{
    b.setDate(b.getDate()+1);
    alert(b.getDate());
}
f1(a);
alert(a.getDate());
Answer

My understanding is that there are 3 scopes: global scope, available globally; local scope, available to an entire function regardless of blocks; and block scope, only available to the block, statement, or expression on which it was used. Global and local scope are indicated with the keyword 'var', either within a function or outside, and block scope is indicated with the keyword 'let'.

For those that believe there is only global and local scope, please explain why Mozilla would have an entire page describing the nuances of block scope in JS.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

Answer

In JavaScript there are two types of scope:

  • Local scope
  • Global scope

The Below function has a local scope variable carName. And this variable is not accessible from outside of the function.

function myFunction() {
    var carName = "Volvo";
    alert(carName);
    // code here can use carName
}

The Below Class has a Global scope variable carName. And this variable is accessible from everywhere in the class.

class {

    var carName = " Volvo";

    // code here can use carName

    function myFunction() {
        alert(carName);
        // code here can use carName 
    }
}
Answer

In EcmaScript5, there are mainly two scopes, local scope and global scope but in EcmaScript6 we have mainly three scopes, local scope, global scope and a new scope called block scope.

Example of block scope is :-

for ( let i = 0; i < 10; i++)
{
 statement1...
statement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.
}
Answer

ECMAScript 6 introduced the let and const keywords. These keywords can be used in place of the var keyword. Contrary to the var keyword, the let and const keywords support the declaration of local scope inside block statements.

var x = 10
let y = 10
const z = 10
{
  x = 20
  let y = 20
  const z = 20
  {
    x = 30
    // x is in the global scope because of the 'var' keyword
    let y = 30
    // y is in the local scope because of the 'let' keyword
    const z = 30
    // z is in the local scope because of the 'const' keyword
    console.log(x) // 30
    console.log(y) // 30
    console.log(z) // 30
  }
  console.log(x) // 30
  console.log(y) // 20
  console.log(z) // 20
}

console.log(x) // 30
console.log(y) // 10
console.log(z) // 10
Answer

ES5 and earlier:

Variables in Javascript were initially (pre ES6) lexically function scoped. The term lexically scoped means that you can see the scope of the variables by 'looking' at the code.

Every variable declared with the var keyword is scoped to the function. However, if other function are declared within that function those functions will have access to the variables of the outer functions. This is called a scope chain. It works in the following manner:

  1. When a function look to resolve a variable value it first looks at its own scope. This is the function body, i.e. everything between curly brackets {} (except for variables inside other functions which are in this scope).
  2. If it cannot find the variable inside the the function body it will climb up to the chain and look at the variable scope in the function in where the function was defined. This is what is meant with lexical scope, we can see in the code where this function was defined and thus can determine the scope chain by merely looking at the code.

Example:

// global scope
var foo = 'global';
var bar = 'global';
var foobar = 'global';

function outerFunc () {
 // outerFunc scope
 var foo = 'outerFunc';
 var foobar = 'outerFunc';
 innerFunc();
 
 function innerFunc(){
 // innerFunc scope
  var foo = 'innerFunc';
  console.log(foo);
  console.log(bar);
  console.log(foobar);
  }
}

outerFunc();

What happens when we are trying to log the variables foo, bar, and foobar to the console is the following:

  1. We try to log foo to the console, foo can be found inside the function innerFunc itself. Therefore, the value of foo is resolved to the string innerFunc.
  2. We try to log bar to the console, bar cannot be found inside the function innerFunc itself. Therefore, we need to climb the scope chain. We first look in the outer function in which the function innerFunc was defined. This is the function outerFunc. In the scope of outerFunc we can find the variable bar, which holds the string 'outerFunc'.
  3. foobar cannot be found in innerFunc. . Therefore, we need to climb the scope chain to the innerFunc scope. It also cannot be found here, we climb another level to the global scope (i.e. the outermost scope). We find the variable foobar here which holds the string 'global'. If it wouldnot have found the variable after climbing the scope chain the JS engine would throw a referenceError.

ES6 (ES 2015) and older:

The same concepts of lexically scope and scopechain still apply in ES6. However a new ways to declare variables were introduced. There are the following:

  • let: creates a block scoped variable
  • const: creates a block scoped variable which has to be initialized and cannot be reassigned

The biggest difference between var and let/const is that var is function scoped whereas let/const are block scoped. Here is an example to illustrate this:

let letVar = 'global';
var varVar = 'global';

function foo () {
  
  if (true) {
    // this variable declared with let is scoped to the if block, block scoped
    let letVar = 5;
    // this variable declared with let is scoped to the function block, function scoped
    var varVar = 10;
  }
  
  console.log(letVar);
  console.log(varVar);
}


foo();

In the above example letVar logs the value global because variables declared with let are block scoped. They cease to exist outside their respective block, so the variable can't be accessed outside the if block.

Answer

A very common issue not described yet that front-end coders often run into is the scope that is visible to an inline event handler in the HTML - for example, with

<button onclick="foo()"></button>

The scope of the variables that an on* attribute can reference must be either:

  • global (working inline handlers almost always reference global variables)
  • a property of the document (eg, querySelector as a standalone variable will point to document.querySelector; rare)
  • a property of the element the handler is attached to (like above; rare)

Otherwise, you'll get a ReferenceError when the handler is invoked. So, for example, if the inline handler references a function which is defined inside window.onload or $(function() {, the reference will fail, because the inline handler may only reference variables in the global scope, and the function is not global:

window.addEventListener('DOMContentLoaded', () => {
  function foo() {
    console.log('foo running');
  }
});
<button onclick="foo()">click</button>

Properties of the document and properties of the element the handler is attached to may also be referenced as standalone variables inside inline handlers because inline handlers are invoked inside of two with blocks, one for the document, one for the element. The scope chain of variables inside these handlers is extremely unintuitive, and a working event handler will probably require a function to be global (and unnecessary global pollution should probably be avoided).

Since the scope chain inside inline handlers is so weird, and since inline handlers require global pollution to work, and since inline handlers sometimes require ugly string escaping when passing arguments, it's probably easier to avoid them. Instead, attach event handlers using Javascript (like with addEventListener), rather than with HTML markup.

function foo() {
  console.log('foo running');
}
document.querySelector('.my-button').addEventListener('click', foo);
<button class="my-button">click</button>


On a different note, unlike normal <script> tags, which run on the top level, code inside ES6 modules runs in its own private scope. A variable defined at the top of a normal <script> tag is global, so you can reference it in other <script> tags, like this:

<script>
const foo = 'foo';
</script>
<script>
console.log(foo);
</script>

But the top level of an ES6 module is not global. A variable declared at the top of an ES6 module will only be visible inside that module, unless the variable is explicitly exported, or unless it's assigned to a property of the global object.

<script type="module">
const foo = 'foo';
</script>
<script>
// Can't access foo here, because the other script is a module
console.log(typeof foo);
</script>

The top level of an ES6 module is similar to that of the inside of an IIFE on the top level in a normal <script>. The module can reference any variables which are global, and nothing can reference anything inside the module unless the module is explicitly designed for it.

Answer

There are two types of scopes in JavaScript.

  1. Global scope: variable which is announced in global scope can be used anywhere in the program very smoothly. For example:

    var carName = " BMW";
    
    // code here can use carName
    
    function myFunction() {
         // code here can use carName 
    }
    
  2. Functional scope or Local scope: variable declared in this scope can be used in its own function only. For example:

    // code here can not use carName
    function myFunction() {
       var carName = "BMW";
       // code here can use carName
    }
    

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.