Git Product home page Git Product logo

fewpjs-js-fundamentals-lexical-scoping-nyc-web-career-040119's Introduction

JavaScript Fundamentals: Lexical Scoping

Learning Goals

  • Explain the concept of lexical scoping
  • Demonstrate how lexical scoping informs the scope chain of a function

Introduction

Earlier we discussed the scope chain and how it works. But how does JavaScript decide which outer scope to place into the scope chain for a new function? Knowing what's going on under the surface during the declaration and invocation can be useful to you as a web programmer. Let's take a look at lexical scope.

Explain the Concept of Lexical Scoping

In JavaScript, each declared function creates its own scope. Lexical scope is scope that based on where variables and blocks of scope are defined by the programmer at the time it's written, and solidified by the time the code is processed.

The lexing phase of code compilation determines where and how all identifiers are declared, and how they will be looked up during execution. This mechanism also plays a role in being able to access certain variables throughout the code. For example the statement let foo = 'bar' is split into two separate steps at lexing time:

  1. let foo declares the variable in the scope before code execution.

  2. foo = 'bar' assigns the value 'bar' to the variable foo, if it is found in the available scope.

Demonstrate How Lexical Scoping Informs the Scope Chain of a Function

Take a look at the following code snippet:

const myVar = 'Foo';

function first () {
  console.log('Inside first()');

  console.log('myVar is currently equal to:', myVar);
}

function second () {
  const myVar = 'Bar';

  first();
}

JavaScript looks up the scope chain to perform identifier resolution. Given that information, what do you think will get logged out to the console when we invoke second()? Let's try it out:

second();
// LOG: Inside first()
// LOG: myVar is currently equal to: Foo
// => undefined

Did that seem unexpected? At first glance, it might seem like "Bar" should get printed out. Inside second(), that string is assigned to the myVar variable right before first() is invoked:

function second () {
  const myVar = 'Bar';

  first();
}

However, the assignment of myVar as 'Bar' is not visible to first(). This is because second() is not the parent scope of first().

In the following diagram, the red myVar is declared in the global scope, and the green myVar is declared inside second():

Lexical scope

No variable named myVar exists inside first(). When the JavaScript engine reaches the second line of code inside the function, it has to consult the scope chain to figure out what myVar is:

js console.log('myVar is currently equal to:', myVar);

The engine's first (and only) stop in the scope chain is the global scope, where it finds a variable named myVar. The reference to myVar inside first() is pointed at that external variable, so console.log() prints out myVar is currently equal to: Foo.

first() is declared in the global scope, and, when it comes to the scope chain, JavaScript functions don't care where they are invoked. The only thing that matters is where they are declared. When we declare a new function, the function asks, "Where was I created?" The answer to that question is the outer environment (the outer scope) that gets stored in the new function's scope chain.

In the example above, we typed out our declaration for first() in the global scope, which gets stored in first()'s scope chain. When first() is invoked, the JavaScript engine can't find anything matching myVar locally, so it looked up the scope chain. The engine finds myVar in the outer scope — the global scope — with a value of 'Foo', which is what then gets printed out to the console.

By contrast, if we declare first() inside second(), then first()'s reference to its outer scope points at second() instead of at the global scope:

const myVar = 'Foo';

function second () {
  function first () {
    console.log('Inside first()');

    console.log('myVar is currently equal to:', myVar);
  }

  const myVar = 'Bar';

  first();
}

When we invoke second() this time, it creates a local myVar variable set to 'Bar'. Then, it invokes first():

second();
// LOG: Inside first()
// LOG: myVar is currently equal to: Bar
// => undefined

While first() is executing, it again encounters the reference to myVar and realizes it doesn't have a local variable or function with that name. first() looks up the scope chain again, but this time first()'s outer scope isn't the global scope. It's the scope of second() because first() was declared inside second(). So first() uses the copy of myVar from the second() scope, which contains the string 'Bar'.

Conclusion

Lexical scope is the scope defined by author-time decisions of where functions are declared. The lexing phase of compilation knows where and how all identifiers are declared, and determine how they will be looked-up during execution.

When a variable contains an unexpected value, understanding the scope chain will save you hours of painful debugging. When you're wondering where to declare a function so that it can access the proper variables, your familiarity with JavaScript's lexical scoping will be useful.

Resources

fewpjs-js-fundamentals-lexical-scoping-nyc-web-career-040119's People

Contributors

jenmyers avatar sgharms avatar maxwellbenton avatar

Watchers

James Cloos avatar Kevin McAlear avatar  avatar Mohawk Greene avatar Victoria Thevenot avatar Belinda Black avatar Bernard Mordan avatar raza jafri avatar  avatar Joe Cardarelli avatar The Learn Team avatar Sophie DeBenedetto avatar  avatar  avatar Matt avatar Antoin avatar Alex Griffith avatar  avatar Amanda D'Avria avatar  avatar Nicole Kroese  avatar Kaeland Chatman avatar Lisa Jiang avatar Vicki Aubin avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.