Skip to content

Instantly share code, notes, and snippets.

@0bie
Created October 6, 2016 21:44
Show Gist options
  • Save 0bie/4b68717564efd3c47c3c192df9f26bc8 to your computer and use it in GitHub Desktop.
Save 0bie/4b68717564efd3c47c3c192df9f26bc8 to your computer and use it in GitHub Desktop.
Scope Resolution in JS
Scope Resolution
  • The ability to store values and pull values out of variables is what gives a program state.

  • A well defined set of rules for storing variables in some location, and for hiding those variables at a later time: SCOPE

  • Abstract Syntax Tree (AST): The grammatical structure of a program

  • Any snippet of JS has to be compiled before (usually right before) it's executed

  • Scope collects and maintains a lookup list of all the declared identifiers (variables), and enforces a strict set of rules as to how these are accessible to currently executing code.

  • The same identifier name can be specified at multiple layers of nested scope, which is called 'shadowing' (the inner identifier 'shadows' the outer identifier)

  • var a = 2;

    • Encountering var a compiler asks scope to see if a variable a already exists for that particular scope collection. If so, compiler ignores this declaration and moves on. Otherwise, compiler asks scope to declare a new variable called a for that scope collection.

    • Compiler then produces code for engine to later execute, to handle the a = 2 assignment. The code engine runs will first ask scope if there is a variable called a accessible in the current scope collection. If so, engine uses that variable. If not, engine looks elsewhere.

    • If engine eventually finds a variable, it assigns the value 2 to it. If not, engine will raise its hand and yell out an error!

  • For variable assignment two distinct actions take place:

    • First, compiler declares a variable (if not previously declared in the current scope)

    • Second, when executing, engine looks up the variable in scope and assigns to it if found

  • LHS v. RHS lookup

  • Side of what? Of an assignment operation (=) but not literally since assignments can happen through several other ways in JS

  • The LHS lookup is trying to find the variable container itself so that it can assign

  • RHS doesn't really mean 'right hand side of an assignment' per se, it just, more accurately, means 'not left hand side'.

  • You could also think RHS means 'retrieve the source (value)', implying that RHS means 'go get the value of...'

console.log(a):

  • The reference to a here is an RHS reference, because nothing is being assigned to a here. Instead, we're looking up to retrieve the value of a, so that value can be passed to console.log()

  • a = 2:

  • By contrast, the reference to a here is an LHS reference, because we don't actually care what the current value is, we simply want to find the variable as a target for the = 2 assignment operation

  • LHS: who's the target of the assignment a = 2 (assign)

  • RHS: who's the source of the assignment console.log(a) (retrieve)

  • The simple rules for traversing nested scope:

    • Engine starts at the currently executing scope, looks for the variable there, then if not found, keeps going up one level, and so on. If the outermost global scope is reached, the search stops, whether it finds the variable or not.
  • LHS and RHS look-ups behave differently in the circumstance where the variable has not yet been declared (is not found in any consulted scope). E.g

function foo(a) {
  'use strict';
  console.log(a + b);
  b = a;
}

foo(2)
  • When the RHS look-up occurs for b the first time, it will not be found. This is said to be an 'undeclared' variable, because it's not found in the scope.

  • If an RHS look-up fails to ever find a variable this results in a ReferenceError

  • In non-strict mode if an LHS look-up fails the global scope will create a new variable of that name

  • Strict mode (use strict;) disallows the automatic/implicit global variable creation, therefore based on the code above a LHS look-up of an undeclared variable will also return a ReferenceError.

  • If a variable is found for an RHS look-up, but you try to do something with its value that's impossible, such as trying to execute-as-function a non-function value, or reference a property on a null or undefined object, then engine throws a TypeError

  • ReferenceError is scope resolution-failure related, whereas TypeError implies that scope resolution was successful, but there was an illegal/impossible action attempted against the result.

  • No matter where a function is invoked from, or even how it is invoked, its lexical scope is only defined by where the function was declared.

  • Principle of Least Privilege:

    • This principle states that in the design of software, such as the API for a module/object, you should expose only what is minimally necessary, and 'hide' everything else.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment