This is the 15th day of my participation in the August Text Challenge.More challenges in August

1.1 Compilation Principles

  • Word segmentation/lexical analysis Is the process of breaking a string of characters into meaningful code blocks such as var x = 2 into var,x,=,2, character by character. The main difference between word segmentation and lexical analysis lies in whether the recognition of lexical units is carried out stateful or stateless.

  • Parsing/parsing involves turning lexical units into a hierarchy of elements called abstract grammar trees (AST) that represent the grammar structure of your program

  • Code generation The process of parsing the generated syntax tree into executable code is called code generation. JavaScript engines are more complex than this. JavaScript is an interpreted language and has performance issues, but Google’s V8 engine has been optimized to be closer to C++/C and other languages

1.2 scope

Simply put, this is the area of code that can be accessed

Let’s say we declare var x = 2

//global
console.log(this) // Point to the empty object null, global is the top-level scope
var x = 2
function func1() {
  console.log(this) // Point to global, to the scope in which func1 was defined
  for (let i = 0; i < 123; i++) {
    console.log(i) // This is the scope of the for loop
  }
  console.log(i) //ReferenceError: I is not defined, let block scope is not accessible, also known as scope dead
  func2()
}
func1()
Copy the code

Compiler (on variable declarations)

There are LHS and RHS queries, namely lvalue queries and rvalue queries (rvalue is a technical term in C). Var x = 2 (x = 2, x = 2, x = 2, x = 2, x = 2, x = 2) The purpose is to find an assignment target for 2, and console.log(b) will perform an RHS query to find out who, or where, b is, the reference address of console.log(). If the RHS does not find the desired variable in any scope, it will raise an exception, which is a ReferenceERROR. But LHS creates a variable in the top-level scope if it doesn’t find one either. (Strict mode throws errors just as RHS does.) There is an implicit note to note here:

function foo(a){
  console.log(a)
}
foo(2)
Copy the code

Lexical scope

Here are two key words:

  • The lexical change
  • The shadowing effect lexical scope is determined by where you write the variable and block scopes when writing code. The shadowing effect is that if a variable with the same name exists in the current scope and in the upper scope, if the variable is called in the current scope, the variable in the current scope overshadows the variable in the outer scope
var a = 1
function f1(a) {
  var a = 2 // If you comment it out, it will print 1
  console.log(a) / / 2
}
f1()

Copy the code

There are two deception morphologies in lexical scope: eval() and with()

function foo(str, a) {
  eval(str) //eval() parses the arguments into js code and runs.
  // In strict mode. The eval() runtime has its own lexical scope so that its arguments do not change the scope
  console.log(a, b)
}
var b = 2
foo('var b=123'.234) //
Copy the code

And with ()

function foo(obj) {
  with (obj) {
    a = 2}}var o1 = {
  a: 3,}var o2 = {
  b: 3,
}
foo(o1)
console.log(o1.a)
foo(o2)
console.log(o2.a) //undefined
console.log(a) //2,a is leaked into the global scope
Copy the code

With () can treat an object with no or multiple properties as a fully isolated lexical scope. When we pass o1 to with(), o1 has an a property inside it and is assigned directly, while when we pass O2, o1 has no property inside, then we perform LHS query to the global scope assignment. With () actually creates a new lexical scope for the variable passed to it.

this

This is a common misunderstanding

  • 1. This points to itself
  • 2. This refers to its scope (this is confusing, true in some cases, false in others)
This binding rule
  • The default binding
function f1() {
  console.log(this.a)
}
var a = 2
f1() / / 2
Copy the code
  • Implicit binding
function f1() {
  console.log(this.a)
}
var obj = {
  a: 2.f2: f1,
}
obj.f2()
Copy the code

A common problem with implicit binding is that implicitly bound functions lose the binding object and reference the default binding

function foo() {
  console.log(this.a)
}
var obj = {
  a: 3.foo: foo,
}
var bar = obj.foo
a = '123'
bar()

//The argument passing here is an implicit assignment. Functions are also implicitly assigned when passed infunction foo() {
  console.log(this.a)
}
function DoFoo(f) {
  f()
  obj.foo()
}
a = '123'
obj = {
  a: 223.foo: foo,
}
obj.foo()
DoFoo(obj.foo)
Copy the code

Another problem is that it’s very easy to lose this in callback functions, and in some callback functions, to modify the reference to this to determine the priority of this

  • Is the function called in new, (new binding)? If so, this binds to the newly created object
  • var bar = new foo()
  • Is the function called through call(), apply(), or hard binding? If so, this binds to the specified object
  • var bar =foo.call(obj)
  • Is the function called in a context object (implicitly bound)? If so, this binds to that context object
  • var bar = obj1.foo()
  • If not, use the default binding, or if in strict mode, bind to undefined, responsible for binding to global objects.
  • var bar = foo()

The ignored this

If call(),apply(),bind(), the first argument is passed null or undefined, then the binding is the default binding. A common way to do this is to use apply(null,[… For example, this points to a global scope, which unknowingly modifs global variables. A safe way to use this is to create an empty object with object.create(null) to point to this.

Indirect reference

function foo() {
  console.log(this.a)
}
var o1 = {
  a: 1,}var o2 = {
  a: 3.foo: foo,
}
var o3 = {
  a: 4,
}
o2.foo() / / 3; (o3.foo = o2.foo)()//undefined
Copy the code

The assignment expression O3.foo =o2.foo returns a reference to the target function, so it is called at foo() instead of o3.foo(), and the default binding is used, binding to the global scope, which does not define a, and output undefined.

This lexical

Function (){}; function(){};

function foo(a) {
  return (a) => {
    console.log(this.a)
  }
}
var obj1 = {
  a: 2,
}
var obj2 = {
  a: 3,
}
var bar = foo.call(obj1)
bar.call(obj2) / / 2
Copy the code

The arrow function inside foo that returns this captures foo() ‘s this, and foo’s this binds to obj1, so bar’s call() is invalid.