Things I Learned from You Don’t Know JS: Scope & Closures, Appendix A-C

Here are some things I learned from Appendix A-C of You Don’t Know JS: Scope & Closures by Kyle Simpson.

Appendix A: Dynamic Scope

JavaScript has lexical scope, meaning that the scope chain is based on where the functions are declared at author-time. Some programming languages have dynamic scope that’s based on where the function is called at runtime. The this mechanism in JavaScript is similar to dynamic scope as it cares about how and where the function was called.

Appendix B: Polyfilling Block Scope

You can polyfill let with try/catch. This seems ugly but it works. In a wider sense, this is the correct approach to make ES6 work when it’s not yet fully supported everywhere.

Traceur

Google maintains a project called Traceur. It transpiles ES6 features into pre-ES6 code. For example, it takes in a let expression and turns it into try/catch.

Implicit vs. Explicit Blocks

There’s an alternative form of let called the let block or let statement. It isn’t supported in ES6 but it helps code maintainability. It looks like this:

let (a = 2) {
  console.log( a );
}

The author Kyle Simpson has built a tool called let-er that transpiles let statements into runnable code.

Performance

Why try/catch instead of an IIFE? try/catch is slower after all.

There are two reasons:

  1. There’s no reason try/catch should be slower or that it will be slower in the future. It’s officially approved, and the Chrome team is working on making it faster.
  2. Inside a function, this, return, break and continue will get a different meaning and that will likely mess up the code.

Of course you can ignore block-scoping and keep using var everywhere if you want. However, block scope is the future of JavaScript.

Appendix C: Lexical-this

ES6 adds a function declaration syntax called arrow function:

var foo = a => {
  console.log( a );
};

foo( 2 ); // 2

Arrow functions have a special this binding: the immediate lexical enclosing scope.

However, this feature might be confusing when this is used with both regular and arrow functions at the same time. To make the code less confusing, you can use function(){...}.bind( this ) to achieve the same outcome.

Things I Learned from You Don’t Know JS: Scope & Closures, Chapter 5

Here are some things I learned from Chapter 5: Scope Closure of You Don’t Know JS: Scope & Closures by Kyle Simpson.

Nitty Gritty

Whatever facility we use to transport an inner function outside of its lexical scope, it will maintain a scope reference to where it was originally declared.

Loops + Closure

Example: You have a setTimeout function in a for(var i = 0; i <= 5; i++ ) { ... } loop. The callback function will refer to i as 6 at each iteration. That's because i belongs to the scope outside the loop and will be 6 by the time each one of the callback functions is executed.

One solution is to create an IIFE that has a new var declaration that copies the value of i. This way, the scope of the callback function will be different at each iteration.

Block Scoping Revisited

[Ok, this is cool:] There's a more elegant solution: for(let i = 0; ...) { ... }. As we learned in the previous chapter, let creates a new scope at each iteration of the loop, so the callback will have closure over each specific value of i. [I love this.]

Modules

Create a function, add variables and functions inside it and return an object that has some of these functions as its properties, and you have a module. The returned functions are the module's public API and they have closure over the module's private identifiers. They can also modify everything inside the module's scope.

Modules have two requirements:

  1. There must be on outer enclosing function that is executed at least once.
  2. The enclosing function must return at least one inner function that has closure over the private scope.

Modern Modules

Module managers use function.apply(thisArg, argsArray) to invoke wrapper functions and store their return objects. This way, there' a hierarchy of modules and closures, and private and public APIs. It may seem complex and mysterious but if you take a closer look at the implementation, you'll notice that it's based on ordinary modules. The only difference is invoking the function definition wrapper, and if you understand that, you'll understand how module managers work.

Future Modules

ES6 added first-class syntax support for the concept of modules. A file can be loaded via the module system. Each file acts as a separate module, and you can import and exports functions between the modules.

If you want to import one (exported) function, use import function from "module". If you want to import a whole module, use module module from "module". In the latter case, you can reference a function as a property of the module object.

Function-based modules aren't a statically recognized pattern, so their API isn't considered until runtime. This allows you to modify a module's API during runtime. By contrast, ES6 Module APIs are static, so full modules are checked for errors or missing functions during compilation.

ES6 modules must be defined in separate files, one per module. The browser/engine has a module loader that synchronously loads a module when it's imported. The contents inside the module file are treated as if enclosed in a scope closure.

Things I Learned from You Don’t Know JS: Scope & Closures, Chapter 4

Here are some things I learned from Chapter 4: Hoisting of You Don’t Know JS: Scope & Closures by Kyle Simpson.

The Compiler Strikes Again

Regardless of their position in the code, variable and function declarations are processed first in each scope. Assignment, on the other hand, is processed in place. This also applies to a combined declaration and assignment, like var a = 2;.

While function declarations are hoisted, function expressions are not.

Functions First

Functions are hoisted first, and then variables. Avoid duplicate declarations, especially between variables and functions.

Functions declared inside an if block used to be hoisted to the enclosing function scope regardless of the true/false value of the if statement. However, ES6 brought a new standard: only functions inside a true if block are declared and they are limited to the block scope.

You should never declare functions inside a block without strict mode. Even then, you should more preferably assign a function expression to a variable. [Functions – JavaScript | MDN]

Things I Learned from You Don’t Know JS: Scope & Closures, Chapter 3

Here are some things I learned from Chapter 3: Function vs. Block Scope of You Don’t Know JS: Scope & Closures.

Scope From Functions

It doesn’t matter where in the scope a declaration appears. The variable or function belongs to the containing scope bubble regardless.

Hiding In Plain Scope

There’s a software design principle called Principle of Least Privilege. It means that you should only expose what’s truly necessary in the public API and hide everything else inside a (function) scope. This results in better and safer software.

Collision Avoidance

Example: You create a for(var i = 0; i < 10; i++) { ... } loop. The i variable belongs to the scope containing the loop. If you use a variable called i elsewhere in that scope, it references to the same i that was declared in the loop. An example would be assigning a value in a nested scope without var. This may cause an infinite loop or other unexpected behavior. So always use var unless you specifically need to access a variable from an outer scope. It lets you use the same variable name because of shadowing.

Global "namespaces"

When using multiple libraries, identifiers with the same name will collide with each other. For this reason, global variables are often put inside an object that creates a "namespace" and prevents collision.

Module Management

A more modern "module" approach is using a dependency manager. This way, no library ever adds any global identifiers but instead they are imported into a specific scope. You can also use the same idea in your own code without actually using any of these managers.

Functions As Scopes

The first step to apply the Principle of Least Privilege is to hide identifiers inside a function. However, there are two problems: the global scope is polluted with the function name and you need to call the function. To solve this, use IIFEs. As the function name is contained within the IIFE scope, it solves both problems.

How to distinguish a function declaration and expression? If function is the first thing in the statement, it's a declaration. Otherwise, it's an expression.

Anonymous vs. Named

Anonymous function expressions have several drawbacks:

  1. They leave no name in stack traces so debugging is more difficult.
  2. There's only the deprecated arguments.callee method to reference the function to itself in recursion or self-unbinding.
  3. The readibility of the code suffers.

So, always name your function expressions.

Invoking Function Expressions Immediately

There are two alternative ways to write IIFEs: (function foo(param){ ... })(); or (function foo(param){ ... }());. Both are functionally identical and the difference is stylistic.

It's possible that undefined gets compromised if there's an identifier of the same name that has some other value. If you run an IIFE with the argument undefined so that you don't pass any value to it, you have a "safe" undefined inside the IIFE's scope.

Blocks As Scopes

Many programming languages other than JavaScript support Block Scope. It means that variables declared inside an if statement, for example, only belong to that block. In JavaScript this is also a common practice but the variable's scope is the enclosing function nevertheless.

However, there are ways to limit the scope to a smaller block than the enclosing function.

with

Although not recommended to use, with creates a scope that only exists for the lifetime of the with statement.

try/catch

Variable declarations in the catch clause of a try/catch are block-scoped to the catch block. However, many linters annoingly complain of duplicate variable names in catch blocks.

let

ES6 introduced let that declares the variable only in the current block (usually inside { }). When using let, it's recommended to create an arbitrary { } block. This makes moving code around at a later time easier and more clear.

Declarations made with let don't hoist to the entire scope. They are not defined until the declaration statement.

Garbage Collection

If you have an event listener, for example, the listener callback function has a closure over the entire scope. This means that even variables that the function doesn't need may be stored for the lifetime of the closure. To avoid this, put variables that are not needed in the closure inside a block. This way, they won't be alive when the closure is created.

let loops

When you use let in a for loop, it binds the iteration variable to each iteration of the body, making its scope unique to that iteration. This assures that the value is re-assigned at the end of each iteration.

When using let, be careful when re-factoring code. You might take the variable out of its scope more easily than when using var.

const

Variables created with const are also block-scoped. Any attempt to change a const value at a later time results in a TypeError.

Things I Learned from You Don’t Know JS: Scope & Closures, Chapter 2

Here are some things I learned from Chapter 2: Lexical Scope of You Don’t Know JS: Scope & Closures.

JavaScript employs a model called Lexical Scope. There’s also something called Dynamic Scope but that’s not so common in modern programming languages.

Lex-time

Lexical scope is scope that is defined at lexing time. It’s based on where variables and blocks of scope are authored by the programmer at write time and mostly set in stone.

You can think of nested scopes as bubbles inside each other. Each bubble is strictly nested: it can’t cross scope boundaries.

Look-ups

Scope look-up stops once it finds the first match. The same identifier name can be specified at multiple layers of nested scope, which is called shadowing (the inner identifier shadows the outer one). Scope look-up always starts at the innermost scope and works it way outward until it finds the first match.

To bypass shadowing and access a global variable, you can use [in front-end code, note by JN] window.identifier. This works only for global variables; non-global shadowed variables can’t be accessed.

Lexical scope is only defined by where the function was declared, regardless of where and how it is invoked.

The lexical scope look-up process only applies to first-class indentifiers. For example, referencing to foo.bar.baz would cause the engine to look for foo with lexical scope and bar and baz properties with object property-access rules.

Cheating Lexical

There are two mechanisms that allow cheating lexical scope at runtime. However, they lead to poorer performance and are generally not recommended.

eval

With eval(string) you can pass in some code in string format. The program will treat it as authored code in the place of the eval function. That way, you can create new stuff in that scope at runtime.

In strict mode, eval doesn’t modify the enclosing scope, though.

It’s also possible to pass in a string as the first argument setTimeout and setInterval functions that’s treated the same way as strings passed in to eval. Don’t do it! This is a legacy feature that’s been deprecated a long time ago.

The new Function() constructor can also take a string of code in its last argument. This too should be avoided.

Creating dynamic code is almost never recommended because of the decrease in performance.

with

The with (obj) { ... } keyword takes in an object and turns it into a scope. So, if you try to modify a property that can’t be found inside the object, the normal scope look-up rules apply. This way, it’s possible to implicitly create a global variable by using with even though that variable was never declared in any scope that existed when the code was authored.

The with keyword is deprecated. Furthermore, when using strict mode, it’s completely disallowed. So don’t use that, either.

As a curiosity, if you declare a variable using var inside with, that variable belongs to the containing function’s scope, not just the with scope.

Performance

There are various optimizations the JavaScript engine does when compiling code. These include pre-determining where the variables and function declarations are. This speeds up resolving identifiers during execution significantly. However, if you use eval() or with, the engine doesn’t perform these optimizations at all because they would be pointless if scopes are altered during runtime anyway.

Things I Learned from You Don’t Know JS: Scope & Closures, Chapter 1

Here are some things I learned from Chapter 1: What is Scope? of You Don’t Know JS: Scope & Closures.

Scope means the set of rules for storing variables in some location and finding them at a later time.

Compiler Theory

In traditional compiled-language process, there are roughly three steps:

  1. Tokenizing/Lexing: Breaking up a string of characters into tokens. In var a =
    2;
    , these tokens would probably be var, a, =, 2 and ;.
  2. Parsing: Taking a stream of tokens and turning it into a tree of nested elements. This tree is called an AST (Abstract Syntax Tree).
  3. Code-Generation: The process of taking an AST and turning it into executable code.

The JavaScript engine and many other compilers include additional steps like optimizing the performance of the execution. What makes JavaScript different from many other compiled languages is the compilation speed. The code is often compiled just before execution in mere microseconds.

Understanding Scope

Let’s think of scope as a conversation. We’ll continue using the program var a = 2; as an example.

The Cast

The conversation happens between three “characters”:

  1. Engine: responsible for start-to-finish compilation and execution of the program.
  2. Compiler: handles the dirty work of parsing and code-generation.
  3. Scope: collects and maintains a look-up list of all identifiers and enforces a strict set of rules as to how these are accessible.

Back & Forth

In our example program, there are two distinct actions:

  1. Compiler asks Scope to see if a variable a already exists for that scope collection. If so, Compiler ignores the declaration and moves on. Otherwise, Compiler asks Scope to declare a new variable called a for that scope collection.
  2. Engine receives code from Compiler to later execute. The code will first ask Scope if there is a variable a accessible in the current scope collection. If not, Engine looks elsewhere (see Nested Scope).

If engine finds a variable, it assigns the value 2 to it. Otherwise, it produces an error.

Compiler Speak

There are two kinds of look-ups that Engine performs: LHS (“Left-Hand Side”) and RHS (“Right-Hand Side”). RHS look-ups retrieve a value of a variable and LHS look-ups assign a value to a variable.

Example of LHS: the reference to a in a = 2;.

Example of RHS: the reference to a in console.log( a );.

When calling a function, Engine performs an RHS look-up for the function name and an LHS look-up for assigning the function parameters.

Nested Scope

If Engine can’t find the variable in the current scope, it keeps going up one level until it finds it. If the outermost global scope is reached, the search stops.

Errors

If Engine doesn’t find the variable and it’s reached global scope, one of the following happens:

  • RHS: ReferenceError is thrown.
  • LHS: A global variable is created. (Or if in strict mode, ReferenceError is thrown.)

ReferenceError happens when a variable is not found in Scope. If the variable was found but you try to do an illegal or impossible action with it (executing a non-function variable etc.), a TypeError is thrown.

Things I Learned from You Don’t Know JS: Up & Going, Chapter 2

Here are some things I learned from Chapter 2: Into JavaScript from You Don’t Know JS: Up & Going.

Values & Types

JavaScript has an interesting bug: typeof null returns 'object', not 'null' as it should. The problem is that so much code on the Web relies on the bug that fixing it would break a lot of things.

Objects

Functions

Functions are actually objects. You can store your own named properties to a function like in any object.

Built-In Type Methods

There are object wrapper forms, or natives, that pair with primitive value types. For example, when you reference a method of a string, JavaScript boxes the value to the String object wrapper form behind the scenes and finds the proper method.

Comparing Values

Coersion

Coersion can happen explicitly or implicitly. Explicit coersion is something you can see straight from the code, but implicit can be less obvious and happen as a side effect of a function. For example, '42' * 1 returns 42, so there’s an implicit coersion from number to string.

Equality

Both == and === check for value equality. The difference between the two is that == allows coersion while === doesn’t. That’s why === is called strict equality (== is called abstract equality (1) ).

There’s an actual process by which this coersion is done. (1)

This process allows you to:

  • force string comparison by '' + a == '' + b.
  • force number comparison by +a == +b.
  • force boolean comparison by !a == !b. (1)

The comparison new String('a') == new String('a') evaluates as false because the left and right side refer to two different objects even though their primitive values are the same. (1)

Based on the coersion rules, here are three rules about whether to use == or ===:

  • If either side could be true or false, use ===.
  • If either side could be 0, '' or [], use ===.
  • Otherwise, you are safe to use == and you should do that because it simplifies your code and improves its readability.

Arrays are coerced to strings by joining all the values with commas in between.

Inequality

The inequality operators <, >, <= and >= are always abstract and never strict.

Variables

A valid identifier (variable name) starts with A-Z, a-z, $ or _ and can be followed with either of them plus the numerals 0-9.

Reserved words (for, in, if etc.) can't be used as variable names but are okay as property names.

Function Scopes

Hoisting

Wherever var appears inside a scope, it's conceptually "moved" to the beginning of the scope. The same applies for function declarations.

In the case of var, using variables before they are declared in their scope isn't recommended because it's confusing. However, it's common to call functions before they are declared in the code.

Nested Scopes

Always formally declare your variables using var. Otherwise you'll create automatic global variables which are bad.

If you declare a variable inside an if statement or loop, it still belongs to the whole function. However, if you use let, the scope is restricted to the if statement or loop. Be conscious of scope and you'll make your code easier to maintain.

Strict Mode

Strict mode tightens the rules for some behaviors. It keeps the code safer and more optimizable by the JavaScript engine. You should always use strict mode.

You can use strict mode by writing the line "use strict"; in the scope you want to apply strict mode to.

One example of strict mode in action is that it throws an error if a variable is used without proper declaration and thus prevents an accidental global variable being created.

Functions As Values

While a function can have parameters and a return value, the function itself is also a value that can be stored to a variable. For example, in var x = function() {}; an anonymous function is stored in variable x. Similarly, in var y = function foo() {};, the function is named (foo).

Named function expressions are preferred, although anonymous function expressions are very common.

Immediately Invoked Function Expressions (IIFEs)

The IIFE syntax (function foo(){ // do something })(); is actually analogous to calling a function by name: first comes the actual function (or a reference to one) and then parentheses that run the function.

Like any function, an IIFE creates its own scope. Therefore, IIFEs are often used to declare variables that don't affect code outside the IIFE.

Closure

Understanding closure is one of the most important skills in JavaScript programming.

Modules

You can create modules that have private variables and functions as well as a public API.

Create a module by declaring a function. Variables declared inside the module are its private variables and functions inside the module function are private functions. You can use closure over the module's variables in your inner functions and return the public API as an object containing the module's functions.

Create new instances of the module by assigning the function's return value (the public API) to a variable. This way, closure keeps the private variables alive even after the outer function has finished running and you can use the public API without showing its actual implementation to the outside world.

Note: when creating an instance of the model, don't use new. The model may look like a class but it's not. It's just an ordinary function, and using new would be inappropriate and waste resources.

this Identifier

If a function has a this reference inside it, that this usually refers to an object. A common misconception is that this refers to the function itself. It doesn't.

There are four rules for how this gets set and it depends on how the function is called:

  1. Called from the global scope: this refers to the global object, or in strict mode, undefined (and you'll get an error).
  2. Called from an object: this refers to the object it was called from.
  3. function_name.call( object_name ); sets this to object_name.
  4. new function_name() sets this to a brand new empty object.

Prototypes

When you reference a property on an object and that property doesn't exist, JavaScript will use the object's internal prototype reference to find another object to look for the property on. It's like a fallback if the property is missing.

One way to link a new object to an existing one (and create a prototype reference from the new to the existing) is var newObj = Object.create( existing_obj );.

Prototypes are often (ab)used to emulate class inheritance from other languages. A more natural way is a pattern called behavior delegation where you intentionally design your linked objects to be able to delegate from one to another.

Old & New

When new features are added to JavaScript, there are compatibility issues with some browsers. These issues can often be solved with polyfilling or transpiling.

Polyfilling

Polyfilling needs writing a piece of code that works as a fallback if the browser doesn't support the feature. In other words, you create your own implementation of the feature. Not every new feature can be polyfilled, though. In addition, try to adhere to the specification as strictly as possible.

Transpiling

When new syntax is introduced to JavaScript, you can't polyfill them. The solution is transpiling (short for transforming + compiling). Transpilers take in new code and put out browser-compatible code.

Using transpiling has several benefits:

  • The new syntax is designed to make your code more readable and maintainable.
  • Transpiling allows the use of new syntax on browsers that support it. The new syntax often brings browser optimizations. In addition, browser makers have more real-world code to test their implementations on and improve their products.
  • Using the new syntax provides feedback to the JavaScript committee. If issues are found early enough, they can be fixed before they become permanent (like the null object did).

There are many transpilers that you can use. These include Babel and Traceur. (It seems that Babel is more actively updated as of November 2017.)

Polyfilling and transpiling are good things and you should use them whenever you find the new features useful.

Non-JavaScript

It is not rare that the JavaScript code you write isn't actually implemented in JavaScript. For example:

  • The document object is a so called host object. It looks like JavaScript but it isn't.
  • The DOM API may not be implemented in JavaScript. Instead, it may use C/C++. However, this is browser-specific.
  • I/O functionality like alert or console.log() is implemented in the browser and there's a good chance the language isn't JavaScript.

Additional Sources

(1) ECMAScript Language Specification - ECMA-262 Edition 5.1

What I Learned in October 2017

I recently started an online curriculum called Web Development with Computer Science Foundations – comprehensive. It’s a collection of books, courses and projects that will teach me the skills to become a professional web developer.

I don’t know who’s behind this project, and they clearly keep a low profile. It’s a free, non-profit curriculum that includes a vast amount of material – enough to spend years on. I try to be effective and learn these skills in one year. I don’t know if that’s even possible but I’m going to find out and adapt if needed.

Blogging about What I’ve Learned

One part of the curriculum is that I write down everything I learn and publish it in some way. I’ve decided to put them in my blog and you’ve already seen some of the stuff.

Because I already have some experience about JavaScript and computer science, I’m not writing down everything, just the new things I learn. However, it’s quite a lot. It’s been eye-opening and humbling to start from the very basics and notice how much I don’t know yet. As the saying goes: the more you know, the better you know how little you know.

Some key things I learned in October:

Git

I learned a lot of basic Git commands on Try Git. I had previously only known the very basic things like pull, add, commit and push. The Try Git tutorial taught me a lot more and gave me a better grasp of the potential that Git has, like branches and hooks.

Chrome Developer Tools Console

I learned lots and lots of cool stuff you can do with the developer console. These include count, group, table and time.

JavaScript Basics

The first book in the curriculum is called You Don’t Know JS: Up & Going by Kyle Simpson.

Although the book starts from the very basics, I’ve had to stop and take notes all the time. Things I’ve learned include modules, abstract vs strict comparison, this identifier and various details about theory of how JavaScript works.

Moving on

It takes some time to get the wheels turning and get used to all this new stuff so I might have some trouble keeping up with the schedule in the beginning. In addition, I have some other coding projects, some music stuff and a university course going on so available time is limited.

Having said that, I’m in the process of finishing stuff in order to make this curriculum my full time commitment for the next year or so. I have some savings and a couple freelancing opportunities to keep me alive.

I hope this all turns out fine and also that I’ll be able to share my knowledge. Hopefully I can help someone learn something useful about programming.