JavaScript based

Foundation is the most important thing, part of learning collated from Hu Yui’s blog, address github.com/mqyqingfeng…

JavaScriptData type of

Raw data type:

  • Boolean: true/false

    Data type conversion for Boolean:

    The data type The value converted to true Convert to a value of false
    boolean true false
    string Any non-empty string “(empty string)
    number Any non-zero digit 0 and NaN
    object Any object null
    undefined N/A (not applicable) undefined
  • Null: Indicates an empty object pointer

    Note: The result of typeof null is Object

  • Undefined: undefined value (lives a variable without assigning it)

  • Number: a floating-point number represented by e (scientific notation) for extremely large or extremely small numbers. For example, 3.125e7 is 3.125 times ten to the seventh power

    Due to memory limitations, ECMAScript does not save all the values in the world. The minimum value is in number.min_value – in most browsers, this value is 5E-324; The maximum value exists in number.max_value – in most browsers, this value is 1.7976931348623157e308, which is automatically converted to the special Infinity value (Infinity) if it is exceeded in a calculation, or -infinity if it is negative

    • The integer

    • Floating point (decimal)

    • NaN: non-numeric, which is a special value used to indicate that an operand that was supposed to return a value does not return a value (to avoid throwing errors)

      NaN itself has two unusual features:

      1. Any operation that involves a NaN returns a NaN

      2.NaN is not equal to any value, including itself

    The Number() function is converted as follows:

    • If it isboolean:True and falseRespectively are converted to0 s and 1 s
    • If it’s a number: simply pass in and return
    • If it isnull: returns 0
    • If it isundefinedReturns theNaN
    • If it isstring, follow the following rules:
      • The string contains only numbers (plus and minus signs), converted to base 10, ignoring leading zeros
      • The string contains a valid floating-point format, converted to a floating-point number, ignoring leading zeros
      • String containing a valid hexadecimal format (0xf) to a decimal number of the same size
      • The empty string is converted to 0
      • A string containing characters other than the above is converted toNaN
    • If it is an object, the object’svalueof()Method and then return the value according to the previous conversion rule, if returnedNaN, the object’stoString()Method, and then return again according to the previous conversion rules
  • String: enclosed in single or double quotation marks

    The String() function follows the following conversion rules:

    • If the value istoString()Method is called and the result is returned
    • If the value isnull, the return"null"
    • If the value isundefined, the return"undefined"
  • Symbol: a new data type in ES6. Symbol represents a unique value and is generated by a call to the symbol function

Reference data type

  • Object: A collection of data and functions

    Each instance of Object has the following properties and methods:

    • constructor(): holds the function used to create the current object
    • hasOwnProperty(propertyName): checks whether the given property is in the current object instance (not in the instance’s prototype)
    • isPrototypeOf(object): checks whether the object passed in is a prototype of the current object
    • propertyIsEnumerable(propertyName): Checks whether a given attribute can be usedfor... in...Statement to enumerate
    • toLocaleString(): Returns a string representation of an object that corresponds to the execution environment
    • toString(): Returns a string representation of an object
    • valueOf(): Returns a string, numeric, Boolean representation of an object

Determine the data type of JavaScript

typeof

The Typeof operator is used to determine which data type a value belongs to

typeof 'seymoe'    // 'string'
typeof true        // 'boolean'
typeof 10          // 'number'
typeof Symbol(a)// 'symbol'
typeof null        // 'object' is not null
typeof undefined   // 'undefined'

typeof {}           // 'object'
typeof []           // 'object'
typeof(() = > {})    // 'function'
Copy the code

As can be seen from the above code:

  • nullIs wrong, and the result isobject
  • The operator evaluates the type of an object and its subtypes, such as functions (callable objects), arrays (ordered indexed objects), and so onobject

instanceof

Object types can also be determined by the instanceof operator, which tests whether the constructor’s prototype appears on the prototype chain of the object being tested

[] instanceof Array            // true
({}) instanceof Object         // true
(() = >{}) instanceof Function   // true
Copy the code

But instanceof is not a panacea, for example 🌰 :

let arr = []
let obj = {}
arr instanceof Array    // true
arr instanceof Object   // true
obj instanceof Object   // true
Copy the code

__proto__ === array. prototype; Array is an Object subtype. Array.prototype.__proto__ === Object.prototype, so the Object constructor is on the prototype chain of arR. So instanceof still can’t elegantly determine whether a value is an array or an ordinary object.

Object.prototype.toString()

Through the Object. The prototype. The toString () method to determine the data type

Object.prototype.toString.call({})              // '[object Object]'
Object.prototype.toString.call([])              // '[object Array]'
Object.prototype.toString.call(() = > {})        // '[object Function]'
Object.prototype.toString.call('seymoe')        // '[object String]'
Object.prototype.toString.call(1)               // '[object Number]'
Object.prototype.toString.call(true)            // '[object Boolean]'
Object.prototype.toString.call(Symbol())        // '[object Symbol]'
Object.prototype.toString.call(null)            // '[object Null]'
Object.prototype.toString.call(undefined)       // '[object Undefined]'

Object.prototype.toString.call(new Date())      // '[object Date]'
Object.prototype.toString.call(Math)            // '[object Math]'
Object.prototype.toString.call(new Set())       // '[object Set]'
Object.prototype.toString.call(new WeakSet())   // '[object WeakSet]'
Object.prototype.toString.call(new Map())       // '[object Map]'
Object.prototype.toString.call(new WeakMap())   // '[object WeakMap]'
Copy the code

We can see that this method returns the correct object type for any type of value passed in, but there are a few things to note:

  • The method is essentially a dependencyObject.prototype.toString()Method to get the internal properties of the object[[Class]]
  • Passing in the primitive type makes it possible to determine the result because the value is wrapped
  • null å’Œ undefinedThe ability to output results is handled by the internal implementation

A few questions from the interview

Is NULL an object? Why is that?

Conclusion: NULL is not an object

Explanation: Although Typeof NULL outputs objects, this is a long-standing JavaScript Bug. In the original version of JavaScript, the 32-bit system was used to store the type information of a variable using status for performance purposes. The beginning of 000 indicates that it is an object and the null indicates all zeros, so it is wrongly judged as object

Why can “1”.toString() be called?

Explanation: Several things are done in this statement:

var s = new String(a);// Create a String instance
s.toString();   // Call the instance method
s = null;    // Destroy the instance immediately after execution
Copy the code

This whole process reflects the nature of the basic wrapper types, which fall within the basic data types, including Boolean, number, and String

Why doesn’t 0.1+0.2 equal 0.3?

Explanation: 0.1 and 0.2 will loop forever after being converted to binary, the extra bits will be truncated due to the standard bit limit, and the accuracy deviation has already occurred. After being added, the binary number truncated due to the floating-point decimal limit will become 0.30000000000000004 when converted to decimal

Take a look at the code and ask, what does it output? Why is that?

var a = {};
var b = {key:"a"};
var c = {key:"b"};
a[b] = "123";
a[c] = "456";
console.log(a[b]); / / output 456
// In JavaScript, the key name of an object can only be a string. When b/ C is used as a key name, the toString method will be called first. When B/C calls toString, the result will be [object object]
Copy the code

Manually implement an Instanceof feature:

Core: Upward lookup based on prototype chain

function myInstanceof(left, right) {
    // The basic data type returns false
    if(typeofleft ! = ='object' || left === null) return false;
    //getProtypeOf is a method that comes with Object objects and gets the prototype Object for its parameters
    let proto = Object.getPrototypeOf(left);
    while(true) {
        // Not yet found
        if(proto == null) return false;
        // Find the same prototype object
        if(proto == right.prototype) return true;
        proto = Object.getPrototypeof(proto); }}Copy the code

The with statement in JavaScript

The with statement sets the scope of the code to a specific object as follows:

Statement;

The with statement is not allowed in strict mode, otherwise it will be considered a syntax error

The purpose of defining the with statement is to simplify writing the same object more than once, for example:

var ss = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;
// We can use with
with(location){
  var ss = search.substring(1);
  var hostName = hostname;
  var url = href;
}
Copy the code

The execution context stack of JavaScript

The execution order of JavaScript code

When asked about the order in which JavaScript is executed, most people will have an intuitive impression (top to bottom, left to right)

Let’s look at the following code:

var a = function(){
  console.log("a1")
}
a();  // a1
var a = function(){
  console.log("a2")
}
a();  // a2
Copy the code

Take a look at the following code:

function a(){
  console.log("a1")
}
a();   // a2
function a(){
  console.log("a2")
}
a();   // a2
Copy the code

As we all know from 🌰, JavaScript code is not executed line by line, and there is a “prep” (pre-parsing of the code) in between, such as variable promotion in the first code and function promotion in the second code.

Execution context type

There are three types of execution contexts in JavaScript:

  • Global execution context: This is the default context in which any code that is not inside a function is executed. It does two things: createwindowObject (in the browser environment) and setthisIs equal to the global object. There is only one global execution context in a program.
  • Function execution context: Whenever a function is called, a new context is created for the function. Each function has its own execution context, but is created when the function is called. Function contexts can have as many as they want (functions can be called multiple times). Whenever a new execution context is created, it executes the code in the defined order
  • Eval code: executes inevalThe code inside a function also has its own execution context (not often used in development)eval)

Execution context properties:

When the JavaScript engine executes a piece of executable code, it creates a corresponding execution context. For each execution context, it has three important properties:

  • Variable Object (VO)
  • Scope chain
  • this

Execution context stack (execution stack)

The execution stack, that is, the call stack. We all know that the stack is LIFO, and the execution stack is used to store all execution contexts created while the code is running.

The first time a JavaScript engine encounters script code, it creates a global execution context and pushes it onto the current execution stack. Every time the engine encounters a function call, it creates a new execution context for that function and pushes it to the top of the stack.

What function is executed by the engine whose execution context is at the top of the stack, and when the execution of the function ends, the execution context pops out of the stack, and the control flow goes to the next execution context in the current stack.

We can simulate the behavior of an execution context stack in the form of an array, defining an execution context stack:

ECStack = [];
Copy the code

When JavaScript begins to interpret execution code, the first thing it encounters is global code, so it starts by pushing a global execution context onto the execution context stack, which we’ll use as a globalContext first. The execution context stack is emptied only when the program ends, so there is always a globalContext at the bottom of the execution context stack until the program ends

ECStack = [
  globalContext
]
Copy the code

Let’s look at the following code:

function fun3() {
    console.log('fun3')}function fun2() {
    fun3();
}

function fun1() {
    fun2();
}

fun1();
Copy the code

When fun1 is called, a new execution context is created and pushed onto the top of the execution context stack. When fun2 is called within the function, a new execution context is created and pushed onto the top of the execution context stack, and so on. Until fun3, the innermost function, completes execution, its execution context pops out of the execution context stack, and then pops out of the execution context stack of fun2, until the execution context is cleared.

The simulation execution context stack changes as follows:

// When the program starts, create a global context and push it to the top of the stack
ECStack = [
  globalContext
]
// call fun1 to create a new execution context to push to the top of the stack
ECStack.push(<fun1> functionContext)
// fun2 is called in fun1 to create a new execution context pushed to the top of the stack
ECStack.push(<fun2> functionContext)
// fun2 calls fun3 to create a new execution context on top of the stack
ECStack.push(<fun3> functionContext)
// fun3 is displayed after execution
ECStack.pop()
// fun2 is displayed after execution
ECStack.pop()
// fun1 is displayed after execution
ECStack.pop()
Copy the code

Think about it

Let’s look at the following two pieces of code to see how they differ:

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();
Copy the code
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();
Copy the code

At first glance, they seem to be the same. They both output local scopes. If we use the idea of execution context above, we can find that their execution context stack changes are different

Let’s simulate the first code:

// When the program starts, create a global context and push it to the top of the stack
ECStack = [
  globalContext
]
// call checkScope to create a new execution context on top of the stack
ECStack.push(<checkscope> functionContext)
// Call f to create a new execution context and push it to the top of the stack
ECStack.push(<f> functionContext)
// f A dialog box is displayed
ECStack.pop()
// checkScope is displayed after execution
ECStack.pop()
Copy the code

Let’s look at the second code:

// When the program starts, create a global context and push it to the top of the stack
ECStack = [
  globalContext
]
// call checkScope to create a new execution context on top of the stack
ECStack.push(<checkscope> functionContext)
// checkScope is displayed after execution
ECStack.pop()
// checkscope returns f and then calls f to create a new execution context on the top of the stack
ECStack.push(<f> functionContext)
// f A dialog box is displayed
ECStack.pop()
Copy the code

Variable objects and active objects in JavaScript, and execution procedures

The variable object

As we’ve seen above, JavaScript creates a corresponding execution context for each point of code executed. For each execution context, there are three important properties:

  • Variable Object –VO
  • Scope chain
  • this

A variable object is an execution context-specific data scope that stores variable and function declarations defined in the context

Since variable objects vary from execution context to execution context, let’s take a look at variable objects in global context and variable objects in function context (active objects)

Global context

The global context is the global object, and in the browser environment, the global object is the window object, which has the following properties:

  • This can be referenced and accessed through this

    console.log(this); // window
    Copy the code
  • A global Object is an Object instantiated by the Object constructor

    console.log(this instanceif Object); // true
    Copy the code
  • Global variables point to global objects

    var a = 1;
    console.log(this.a);  / / 1
    Copy the code

In the client environment, the global object has a window attribute pointing to itself

var a = 1;
console.log(window.a); / / 1

this.window.b = 2;
console.log(this.b); / / 2
Copy the code

Active objects

In the global execution context we define variables as variable objects, while in the function context we refer to them as Activation Objects (AO) **

A variable object and an active object are essentially the same thing, and the variable object of that execution context is only activated when it enters the function execution context, the active object, which is initialized via the function arguments property

The execution of JavaScript

The whole process of executing JavaScript code can be divided into two stages:

  • Analysis phase: The execution context is just entered, and the code has not yet been executed

    The variable object at this point will include:

    • All arguments to a function (if function context)
      • Properties of a variable object consisting of a name and corresponding value are created
      • No arguments, property set toundefined
    • Function declaration
      • Properties of a variable object consisting of a name and corresponding value (function object) are created
      • If the variable object already has an attribute of the same name, replace the attribute completely
    • Variable declarations
      • An attribute of a variable object consisting of a name and its corresponding value (undefined) is created
      • If the variable name is the same as an already declared parameter or function, the variable declaration does not interfere with such attributes that already exist

    An 🌰 :

    function foo(a) {
      var b = 2;
      function c() {}
      var d = function() {};
    
      b = 3;
    
    }
    
    foo(1);
    Copy the code

    The AO of the above code in the analysis phase is:

    AO = {
        arguments: {
            0: 1.length: 1
        },
        a: 1.b: undefined.c: reference to function c(){},
        d: undefined
    }
    Copy the code
  • Execution phase: JavaScript finds each variable object declaration assigned to the AO object during the execution phase

    🌰 above, after going through the execution phase, the AO object is modified to:

    AO = {
        arguments: {
            0: 1.length: 1
        },
        a: 1.b: 3.c: reference to function c(){},
        d: reference to FunctionExpression "d"
    }
    Copy the code

Scope and scope chains in JavaScript

scope

A scope is the accessible range of variables, functions, and objects in a particular part of your code at runtime. In other words, scopes determine where variables and functions can be accessed, that is, scopes control the visibility and life cycle of variables and functions. JavaScript uses lexical scoping, also known as static scoping

There are two kinds of scopes in JavaScript

  • Global scope
  • Local scope

In simple terms, can use curly braces {} to distinguish whether a variable is the local scope, if a variable or function in {} statement, then it will belong to the scope of the external, internal and accessible, the local scope before the ES6 contains only function scope, block-level scope provided by the ES6 also belong to the local scope

Static and dynamic scopes

Let’s look at the concepts of static and dynamic scoping first, because JavaScript uses lexical scoping, and the scope of a function is determined at the time the function is defined. As opposed to lexical scope, the scope of a function is determined at the time the function call is executed. An 🌰 :

var a = 1;

function test1(){
  console.log(a)
}

function test2(){
  var a = 2;
  test1();
}

test2();
// The result is...? 1
Copy the code

Since JavaScript uses lexical scope, that is, static scope, let’s examine the execution process:

  1. Executive functiontest2, define variablesa, execute the functiontest1, output variablea
  2. In the outputaFirst look for variables in the function scopeaIf you don’t find it, goDefines the hierarchy above the current functionSearch, and you will finda = 1

Assuming JavaScript is dynamically scoped, the execution would be:

  1. Executive functiontest2, define variablesa, execute the functiontest1, output variablea
  2. In the outputaFirst look for variables in the function scopeaIf you don’t find it, goCall the level above the current functionSearch, and you will finda = 2

Note: The difference between static and dynamic scoping is that static scoping looks up variables at their defined location, while dynamic scoping looks up variables at their calling location

Global scope

Objects and variables that have a global scope can be accessed anywhere in the code. There are two cases of global scope in JavaScript:

  • Functions and variables in the outermost layer of code

    var aa = 1;
    function test(){
      // Inside can access outside
      console.log(aa)
    }
    // The definition can also be accessed outside of external variables, with global scope
    console.log(aa)
    Copy the code
  • Var, let, const are not used to declare variables that are directly assigned

    function test(){
      aa = 1;
      console.log(aa)
    }
    // Directly assigned variables default to global variables
    console.log(aa)
    Copy the code

Local scope

In contrast to global scopes, local scopes can only be accessed in fixed code blocks, most commonly function bodies.

  • Function scope

    function test(){
      var aa = 1;
      // The function body can be accessed internally
      console.log(aa)
    }
    // External access is not available
    console.log(aa)
    Copy the code
  • Block-level scopes (let and const in ES6 can create block-level scopes) Note when using let and const:

    • Let and const have no variable promotion and throw an exception when used first and then declared

      console.log(aa); // Throw an exception
      let aa =1;
      for(let i=0; i<5; i++){console.log(i);
      }
      console.log(i);  // Throw an exception. Let is a block-level scope
      Copy the code
    • Unlike var, let and const do not allow repeated declarations

      var aa = 1;
      var aa = 2;
      console.log(aa); / / 2
      
      var bb = 1;
      let bb = 2;    // Throw an exception
      Copy the code

The scope chain

Now that we’ve talked about how JavaScript code goes through the analysis phase and the execution phase, let’s look at the scope chain.

When executing each function, JavaScript will first look for the corresponding attribute value in the AO object created by itself. If it cannot find it, it will look for the corresponding attribute value in the AO object created by the parent function, and so on until it finds the last object window(global scope). The chain of AO objects formed by this line is called the scope chain

We know that the scope of a function is determined when the function is defined (JavaScript is a statically typed language). This is because there is an internal property [[scope]] inside the function, into which all of the parent variables are stored when the function is created. [[scope]] is the hierarchy of all parent objects, but [[scope]] does not represent the full scope chain

Look at the following 🌰 :

function foo(){
  function bar(){}}Copy the code

When a function is created, the respective [[scope]] is:

foo.[[scope]] = [
  globalContext.VO  // Global variable object
]
bar.[[scope]] = [
  fooContext.AO,  // The active object of the parent function execution context
  globalContext.VO
]
Copy the code

When a function is executed and entered into the function context, VO/AO is created, the live object is added to the front of the scope chain. At this point, the Scope chain of the execution context is named Scope

Scope = [AO].concat([[Scope]])
Copy the code

At this point the scope chain has been created.

An 🌰 :

The function we just created:

var scope = "global scope";
function checkscope(){
  var scope2 = "local scope";
  return scope2
}
checkscope();
Copy the code

This function is executed as follows:

  • Create function checkScope, save scope to inner property [[scope]]

    checkscope.[[scope]] = [
      globalContext.VO
    ]
    Copy the code
  • Execute function checkScope, create function execution context, checkScope function execution context is pushed into the execution context stack

    ECStack = [
      checkscopeContext,
      globalContext
    ]
    Copy the code
  • The checkScope function does not execute immediately, but starts to do preparatory work (the analysis phase) :

    • Copy the function’s [[scope]] property to create the scope chain

      checkscopeContext = {
        Scope:checkscope.[[scope]]
      }
      Copy the code
    • Use Arguments to create an active object, and then initialize the active object, adding parameters, function declarations, and variable declarations

      checkscopeContext = {
        AO: {arguments: {length:0
          },
          scope2:undefined
        },
        Scope:checkscope.[[scope]]
      }
      Copy the code
    • Push the live object into the top of the scope chain of the checkScope function

      checkscopeContext = {
        AO: {arguments: {length:0
          },
          scope2:undefined
        },
        Scope:[AO,[[Scope]]]
      }
      Copy the code
  • After the preparation is complete, start executing the function, modifying the value of the AO as the function executes (the execution phase)

    checkscopeContext = {
      AO: {arguments: {length:0
        },
        scope2:"local scope"
      },
      Scope:[AO,[[Scope]]]
    }
    Copy the code
  • The value of the variable scope2 is found, the function is executed, and the function context is popped from the execution context stack

    ECStack = [
      globalContext
    ]
    Copy the code

We all know that code execution is divided into analysis phase and execution phase. After the analysis phase, the AO (active object) will be transferred to the execution phase, at which time the following operations will be performed:

  • The engine asks the scope if it has a variable
  • If the variable is in scope, the engine uses it
  • If the variable is not in scope, the engine will look up the chain of scopes and throw an error if the variable is not found in the global scope

For example, if the function checkScope returns scope2, the engine will look for the variable in this function scope. If it does not find the variable, the engine will look for the variable in the global scope

The process of finding variables

We all know that engines look up variables along the scope chain, and there are LHS and RHS queries in the process, as explained in JavaScript you Don’t Know (Part 1) :

LHS = Write variable assignment to memory; RHS = Variable lookup or read from memory

LHS and RHS features:

  • Is queried in all scopes
  • In strict mode, the engine throws when the desired variable is not foundReferenceErrorabnormal
  • In the non-strict mode,LHSA global variable is automatically created
  • When the query succeeds, the engine throws if an unreasonable operation (a function call operation on a non-function value) is performed on a variable’s valueTypeErrorabnormal

Flex -grow, flex-shrink, flex-basis