Compiler Theory in JavaScript

Source:https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch1.md

Common Compliation Steps

  • Tokenizing/Lexing: breaking up a string of characters into meaningful (to the language) chunks, called tokens. For instance, consider the program: var a = 2;. This program would likely be broken up into the following tokens: vara=2, and ;. Whitespace may or may not be persisted as a token, depending on whether it's meaningful or not.
    Note: The difference between tokenizing and lexing is subtle and academic, but it centers on whether or not these tokens are identified in a stateless or stateful way. Put simply, if the tokenizer were to invoke stateful parsing rules to figure out whether a should be considered a distinct token or just part of another token, that would be lexing.
  • Parsing: taking a stream (array) of tokens and turning it into a tree of nested elements, which collectively represent the grammatical structure of the program. This tree is called an "AST" (Abstract Syntax Tree). The tree for var a = 2; might start with a top-level node called VariableDeclaration, with a child node called Identifier (whose value is a), and another child called AssignmentExpression which itself has a child called NumericLiteral (whose value is 2).
  • Code-Generation: the process of taking an AST and turning it into executable code. This part varies greatly depending on the language, the platform it's targeting, etc.

Complier Model in JS

  • Engine: responsible for start-to-finish compilation and execution of our JavaScript program.Compiler: one of Engine's friends; handles all the dirty work of parsing and code-generation (see previous section).
  • Scope: another friend of Engine; 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.

LHS & RHS

  • An RHS look-up is indistinguishable, for our purposes, from simply a look-up of the value of some variable.(get the value of)
  • An LHS look-up is trying to find the variable container itself, so that it can assign.
  • In this way, RHS doesn't really mean "right-hand side of an assignment" per se, it just, more accurately, means "not left-hand side".
function foo(a) {

    console.log( a ); // RHS of console Object, LHS of parameter in log

}

foo( 2 ); // RHS of foo

// inplied a = 2 // LHS of a
function foo(a) {

    var b = a; // LHS of b, RHS of a

    return a + b; // RHS of a, RHS of b

}

var c = foo( 2 ); // LHS of c, RHS of foo

// implied a = 2 // LHS of a

Nested Scope

function foo(a) {

    console.log( a + b );

}

var b = 2;

foo( 2 ); // 4

The RHS reference for b cannot be resolved inside the function foo, but it can be resolved in the Scope surrounding it (in this case, the global).

Error

function foo(a) {

    console.log( a + b ); // RHS can not find b in the scopes, results in a ReferenceError being thrown by the Engine

    b = a;

}

foo( 2 );

If the Engine is performing an LHS look-up, and it arrives at the top floor (global Scope) without finding it, if the program is not running in "Strict Mode" [^note-strictmode], then the global Scope will create a new variable of that name in the global scope, and hand it back to Engine.

Strict mode disallows the automatic/implicit global variable creation. In that case, there would be no global Scope'd variable to hand back from an LHS look-up.

If a variable is found for an RHS look-up, but you try to do something with its value that is impossible, such as trying to execute-as-function a non-function value, or reference a property on a null or undefined value, then Engine throws a different kind of error, called a TypeError.

你可能感兴趣的:(JavaScript)