ChatGPT解决这个技术问题 Extra ChatGPT

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?

Here is another nice link to keep in mind this issue: "Explaining JavaScript scope and closures".
Here is an article that explains it very nicely. Everything you need to know about Javascript variable scope
The previously mentioned e-book of Kyle Simpson is available to read on Github, and it tells you all you need to know about JavaScript Scopes & Closures. You can find it here: github.com/getify/You-Dont-Know-JS/blob/master/… It's part of the "You don't know JS" book series, which is great for everybody who would like to know more about JavaScript.
var rules. javascript does not need the "addition" of 'const' and 'let' which are against it's spirit. - I know those two are not a part of your question - had to add this after seeing so many "push" them.

E
E. Reiner

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:

Global - visible by everything Function - visible within a function (and its sub-functions and blocks) Block - visible within a block (and its sub-blocks) 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:

How an identifier was declared Where an identifier was declared Whether you are in strict mode or non-strict mode

Some of the ways identifiers can be declared:

var, let and const Function parameters Catch block parameter Function declarations Named function expressions Implicitly defined properties on the global object (i.e., missing out var in non-strict mode) import statements eval

Some of the locations identifiers can be declared:

Global context Function body Ordinary block The top of a control structure (e.g., loop, if, while, etc.) Control structure body 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 using 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. Note that there is a slight complexity to this. Functions declared as default arguments close over the parameter list, and not the body of the function.

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 (e.g., for the purpose of recursion).

Implicitly defined properties on the global object

In non-strict mode, implicitly defined properties on the global object have global scope, because the global object sits at the top of the scope chain. 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 a 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 scoped 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, implicitly defined properties on the global object are globally scoped. In strict mode, you get an error.

x = 1 // implicitly defined property on the global object (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.


Not even close to being comprehensive, but this is maybe the must-know set of Javascript scope tricks one needs to effectively even READ modern javascript.
A highly rated answer, not sure why. It's just a bunch of examples without proper explanation, then seems to confuse prototype inheritance (i.e. property resolution) with the scope chain (i.e. variable resolution). A comprehensive (and accurate) explanation of scope and property resolution is in the comp.lang.javascript FAQ notes.
@RobG It is highly rated because it is useful and comprehensible to a wide range of programmers, minor catachresis notwithstanding. The link you have posted, while useful to some professionals, is incomprehensible to most people writing Javascript today. Feel free to fix any nomenclature issues by editing the answer.
@triptych—I only edit answers to fix minor things, not major. Changing "scope" to "property" will fix the error, but not the issue of mixing inheritance and scope without a very clear distinction.
If you define a variable in the outer scope, and then have an if statement define a variable inside the function with the same name, even if that if branch isn't reached it is redefined. An example - jsfiddle.net/3CxVm
k
krosenvold

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.


Scope chains are another term for [memory] Closures... for those reading here to learn / get into javascript.
g
gilly3

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.)


I'm afraid to even begin answering this question. As a Real Javascript Programmer, I know how quickly the answer could get out of hand. Nice articles.
@Triptych: I know what you mean about things getting out of hand, but please add an answer anyway. I got the above just from doing a couple of searches... an answer written by someone with actual experience is bound to be better. Please correct any of my answer which is definitely wrong though!
Somehow Jon Skeet is responsible for MY most popular answer on Stack Overflow.
C
Community

Old school JavaScript

Traditionally, JavaScript really only has two types of scope :

Global Scope : Variables are known throughout the application, from the start of the application (*) 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 :

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

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

https://i.stack.imgur.com/J9kEC.png

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.


"IS NOT known" is misleading, because the variable is declared there due to hoisting.
The above example is misleading, variables 'i' and 'j' are not known outside the block. 'Let' variables has scope only in that particular block not outside of the block. Let has other advantages as well, you can't redeclare the variable again and it's hold the lexical scope.
This was helpful, thanks! I think it would be even more helpful to be specific about what you mean by "Modern JavaScript" and "Old school JavaScript"; I think these correspond to ECMAScript 6 / ES6 / ECMAScript 2015, and to earlier versions, respectively?
@JonSchneider : Correct! Where I say "old school JavaScript", I'm ineed talking about ECMAScript 5 and where I'm refering to "modern JavaScript", I'm taking about ECMAScript 6 (aka ECMAScript 2015). I didn't think it was really that important to go into detail here, though, as most people just want to know (1) what's the difference between block scope and functional scope, (2) what browsers support block scope and (3) whether it's safe to use block scope today for whatever project they're working on. So I focused my answer on addressing those issues.
@JonSchneider : (continued) Nevertheless, I just added a link to a Smashing Magazine article on ES6 / ES2015 for those who want to learn more about which features have been added to JavaScript during the last couple of years... of anyone else who might be wondering what I mean with "modern JavaScript".
g
geowa4

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.


J
James McMahon

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.


k
kennytm

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

Yeah, but is it safe to use? I mean would I realistically choose this implementation if my code will run in WebKit?
@Python: No, WebKit doesn't support let.
I guess the only valid use for this would be if you knew all the clients would be using a Mozilla browser like for a companies internal system.
Or if you are programming using the XUL framework, Mozilla's interface framework where you build using css, xml, and javascript.
@GazB even that is a horrid idea! So today you know that your clients are using Mozilla then out comes a new memo stating that now they are using something else. I.E. the reason our pay system sucks... You must use IE8 and never IE9 or IE10 or Firefox or Chrome because it flat out wont work...
T
Travis J

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.

https://i.stack.imgur.com/v45hL.png


Was Javascript really inspired by Hypertalk? I don't recall Hypertalk having such interesting scoping, but the inspiration would perhaps explain Javascript's bizarre operator overloading where 10=="10.0" and 10=="10", but "10.0"!="10". Though Hypertalk's operators behaved even more interestingly.
@supercat - Yessir. Around this time I had been researching the origins of Internet Explorer (which dates back to Mosaic), trying to figure out why IE10 was such a security issue and had sent part of that research on to Jonathan Sampson. Perhaps by coincidence they went on the develop Edge soon after, with many of the suggested security issues removed. This post is actually slightly dated though, because the recent iteration on EcmaScript and the inclusion of microtasks, have created a slightly more involved model with regards to memory management behind the scenes in certain scenarios.
@supercat - For some still available references to that, "I started looking at languages like Logo and Smalltalk and Self and HyperTalk which was Bill Atkinson’s language for HyperCard" -Brendan Eich, "JavaScript (whose creator, Brendan Eich, was inspired by HyperTalk[32])" -Wiki citing his book. Here is the email I wrote to Jonathan at microsoft: jsfiddle.net/fwchpvrj
There may have been some conceptual inspiration, but having worked with both Hypertalk and Javascript, I don't see any design commonality between them. The ability of Hypercard stacks to directly affect the containing system was a result of the fact that upon encountering an unfamiliar command or function, Hypercard would search for a resource with type XCMD or (if memory serves) XFCN whose name matched that of the unfamiliar command or function, and--if one was found--load it into memory as a code resource and call it. By design, any resources that were within the...
...current document would be found by such a search. This made it possible for Hypercard stacks to do things that would not otherwise be possible in the language, but meant that stacks were not sandboxed in any meaningful way. By contrast, web browsers were supposed to provide a sandboxed environment for processing removely-received content; the failure to adequately sandbox was due to bugs, while the fact that Hypercard's didn't sandbox things was a result of a design decision not to restrict the range of tasks stacks could perform.
G
Gerard ONeill

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.


You have some mistakes here, for one JavaScript does have forms of block scoping.
My ears (eyes) are open, Benjamin -- My statements above are how I've been treating Javascript scoping, but they are not based on reading the spec. And I hope you aren't referring to the with statement (which is a form of object scoping), or Mozilla's special 'let' syntax.
Well, with statement is a form of block scoping but catch clauses are a much more common form (Fun fact, v8 implements catch with a with) - that's pretty much the only forms of block scoping in JavaScript itself (That is, function, global, try/catch , with and their derivatives), however host environments have different notions of scoping - for example inline events in the browser and NodeJS's vm module.
Benjamin -- from what I can see, both with and catch only introduce the object into the current scope (and thus the properties), but then after the respective block ends, the variables are reset. But for example, a new variable introduced in a catch will have the scope of the enclosing function / method.
Which is exactly what block scoping means :)
a
austincheney

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:

http://prettydiff.com/jspretty.xhtml?c=white&jsscope

See a demo at:

http://prettydiff.com/jspretty.xhtml?c=white&jsscope&s=http://prettydiff.com/lib/markup_beauty.js

View the code at:

http://prettydiff.com/lib/jspretty.js

https://github.com/austincheney/Pretty-Diff/blob/master/lib/jspretty.js

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


Doesn't work for me with Firefox 26. I paste code or load a file, click execute and nothing happens.
Scope and inheritance are two difference things.
C
CertainPerformance

Inline handlers

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'); } });

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);

Modules (

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.

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.


A
Anshul

JavaScript have only two type of scope :

Global Scope : Global is nothing but a window level scope.Here, variable present throughout the application. 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 -->

Window level - a and outer function are at top level in scope chain. 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.


Not sure why this is not the accepted answer. There is actually just functional scope (prior to ECMA6 there was no "local scope") and global bindings
Y
Yeasin Abedin

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);

J
John Slegers

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


J
James Drinkard

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:

https://i.stack.imgur.com/0kqpZ.png

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


j
jackbean818

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:

try/catch introduce new scope ONLY for the exception variable itself, other variables do not have new scope

with-clause apparently is another exception, but using with-clause it highly discouraged (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with)

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.


C
Community

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.


question about var. "const" and "let" = the destroyers of javascript, are not in the question and should not be in javascript...
Your opinion does not change the validity of the answer. Question is about scoping. const and let are the modern replacements for var which define scope. Neither existed when the question was asked
M
Mig82

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());

k
koredalin

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);

(long time since answer posted ) Block scope ;developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
m
mrmaclean89

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


A
Abdur Rahman

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 
    }
}

const and let are not in the question. why would you bring them up? the question was about vars... const and let are annoying and destroying javascript.
A
Ahmed Khashaba

I really like the accepted answer but I want to add this:

Scope collects and maintains 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.

Scope is a set of rules for looking up variables by their identifier name.

If a variable cannot be found in the immediate scope, Engine consults the next outer containing scope, continuing until is found or until the outermost (a.k.a., global) scope has been reached.

Is the set of rules that determines where and how a variable (identifier) can be looked up. This look-up may be for the purposes of assigning to the variable, which is an LHS (left-hand-side) reference, or it may be for the purposes of retrieving its value, which is an RHS (righthand-side) reference.

LHS references result from assignment operations. Scope-related assignments can occur either with the = operator or by passing arguments to (assign to) function parameters.

The JavaScript engine first compiles code before it executes, and in so doing, it splits up statements like var a = 2; into two separate steps: 1st. First, var a to declare it in that scope. This is performed at the beginning, before code execution. 2nd. Later, a = 2 to look up the variable (LHS reference) and assign to it if found.

Both LHS and RHS reference look-ups start at the currently executing scope, and if need be (that is, they don’t find what they’re looking for there), they work their way up the nested scope, one scope (floor) at a time, looking for the identifier, until they get to the global (top floor) and stop, and either find it, or don’t. Unfulfilled RHS references result in ReferenceError being thrown. Unfulfilled LHS references result in an automatic, implicitly created global of that name (if not in Strict Mode), or a ReferenceError (if in Strict Mode).

scope consists of a series of “bubbles” that each act as a container or bucket, in which identifiers (variables, functions) are declared. These bubbles nest neatly inside each other, and this nesting is defined at author time.


H
HoldOffHunger

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:

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). If it cannot find the variable inside 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:

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. 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'. 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.


M
Mathews Sunny

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.
}

D
Davaakhuu Erdenekhuu

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

const and let are not part of the question. why bring them up? personally, they don't belong in javascript...
@iAmOren Of course they are part of the question, and yes, they do belong in JavaScript. The thing that should be abolished, if anything, is var, but this is neither feasible nor relevant here.
l
ludy

(function foo() { console.log(foo) })(); console.log(typeof foo); // undefined, because `foo` is scoped to its own expression //but, like this (function foo() { console.log('1:', foo) // function foo foo = 100 console.log('2:', foo) // function foo, is not 100, why? })()


z
zangw

There are two types of scopes in JavaScript.

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 } 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 }


This answer was already outdated when it was posted and didn’t add anything new to the existing answers.