Skip to content

Instantly share code, notes, and snippets.

@chadfurman
Last active January 24, 2017 04:22
Show Gist options
  • Save chadfurman/c442591a50a1b98166c5941bdb0b036e to your computer and use it in GitHub Desktop.
Save chadfurman/c442591a50a1b98166c5941bdb0b036e to your computer and use it in GitHub Desktop.
hour2

Scope and Execution Review:

var foo = "bar"; // LHS foo, RHS "bar", foo global scope

function bar() { // remember bar is a declaration in global scope
  var foo = "baz"; // LHS foo, RHS "baz", foo local to bar()
  
  function baz(foo) { // remember `foo` is declared here as local to baz
    foo = "bam";
    bam = "yay";  // remember this is a global scope `bam`
  }
  baz(); // this is a RHS because it's not an LHS
}

bar(); // this execures line 6, ignored during compilation.
foo; // RHS foo, foo in global scope
bam; // RHS bam, bam = "yay"
baz(); // RHS baz, baz is undeclared -- RHS doesn't work the same way as LHS (reference error)

Note: in strict mode, both LHS and RHS return reference errors with undeclared symbols Note: in non-strict mode, only RHS returns reference errors -- undeclared LHS gets declared automatically in global scope

Function Declarations vs Function Expressions

Does the statement start with a function keyword? If so, it's a function declaration. If not, it's an expression. Function expressions should always have a name.

// this is a "named" function expression
var foo = function bar () { // this is an expression -- `bar` does not get declared in the outer scope, but within its own scope
  var foo = "baz"; 
  
  function baz(foo) {
    foo = bar; // this is the enclosing `bar` function, because we have a "named" function expression
    foo; // function...
  }
  baz();
};

foo();
bar(); // error -- bar is not declared in the global scope

my question What about ECMAScript 2015 classes?

Functions are not exactly the smallest atomic unit in JS. Try/Catch has "block scope"

var foo;

try {
  foo.length;
}
catch (err) {
  console.log(err); // TypeError
}

console.log(err); // reference error

Note: Linters complain about re-declaring variables between block-scopes, even though each var is in a different scope.

Lexical Scope

Lexical Scope model -- we've been discussing, it's in most languages Dynamic Scope -- alternative, not present in JS. Bash uses this, Perl has an opt-in for this.

"Lex" in Lexical refers to "Lexing" or parsing during compiliation. Lexical is compile-time scope. Compiler decides what your scope is during compiler time, scope doesn't get decided after that.

What would Dynamic scope look like if we had it in JavaScript? (This is useful when talking about ... this) Lexical, for example, is like looking for something on a floor of a building.
If you don't find it, go up a floor.
When you get to the top, you're out of floors. References are cached during compile time.

Cheating Lexical Scope

Eval -- take any string and treat it like it's code
var bar = "bar";

function foo(str) {
  eval(str); // cheating.  We modify our scope at run-time.
  console.log(bar); // 42, not "bar"
}

foo("var bar = 42;");

When we do this, it slows down our code. The compiler cannot do the optimizations.

Note: in "strict" mode, eval gets its own scope and the code can be better optimized. If you have to ask, do not use eval. Note: setTimeout also uses eval when using the string syntax.

With keyword
var obj = {
  a: 2,
  b: 3,
  c: 4
};

// without the `with` keyword
obj.a = obj.b + obj.c;
obj.c = obj.b - obj.a;

// with the `with` keyword
with (obj) {
  a = b + c; // `a` is defined in the obj
  c = b - a; // `c` is defined in the obj
  d = 3; // ?? d is not defined in the obj, gets created in global scope
}

with keyword creates a whole new lexical scope at run-time Note: in "strict" mode, with is completely disabled.

IIFE Pattern

Immediately Invoked Function Expression Prevents things from getting into the global namespace, avoids collisions

var foo = "foo";

(function($) { // This is still an expression, not a declaration -- we could have given it a name
  // we've ensured that $ effectively points to jQuery, avoiding collisions with other libraries
  var foo = "foo2"; // attached to local function scope, doesn't pollute outer scope.
  console.log(foo); // "foo2"
})(jQuery);  // this immediately executes our expression

console.log(foo);

Runtime says, "I have a function expression

// There are two different stylistic choices.  Functionally the same.
(function(){
})(); // this is one choice, Douglas Croffard calls this "Donkey Balls"
(function(){
}()); // this is the alternative syntax

Block Scope in ES6

ES6+ has "block scope" with "let" -- let will hijack the scope of whatever block we happen to be in.

function foo() {
  var bar = "bar";
  for (let i=0; i<bar.length; i++) { // if this was `var` and not `let`, then `i` would be attached to the function
    console.log(bar.charAt(i));
  }
  console.log(i); // reference error
}

foo();

let lets us declar vars as only being availabe within a block (i.e. if or for etc)

Problems with the let keyword

The let keyword does not "hoist" -- it exists only after it's been declared The let keyword means we have to pay attention to what blocks we declare variables in -- extra mental tax

function foo(bar) {
  if (bar) {
    let baz = bar;
    if (baz) {
      let bam = baz;
    }
    console.log(bam); // Error
  }
  console.log(baz); // Error
}

foo("bar");

Alternative form (rejected by TC39 from ES6):

function foo(bar) {
  let (baz = bar) {
    console.log(baz); // "bar"
  }
  console.log(baz); // error
}

foo("bar");

Alternative alternative:


function foo(bar) {
  /*let*/ { let baz = bar;
    console.log(baz); // "bar"
  }
  console.log(baz); // error
}

foo("bar");

What if we added a tool to make the alternative form, which is not valid JS, work in JS? let-er is a transpiler that adds support for let-blocks -- uses try{throw void 0} catch (foo) { ... } to force block scopes This is the "standard" way to force block-scoping in ES5 -- that is, to use try/catch

Use a transpiler!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment