- Q: Is JS code executed sequentially?
- A: During the execution of JS code, it is necessary to promote variables first, and the reason why variable promotion is needed is that JS code needs to be compiled before execution
1. Variable promotion
Variable and function declarations will be stored in the variable environment, and the default value of the variable will be set to undefined
var scope = 'global scope' function a(){ // 3. The top-level variable environment declares that the scope is initialized to undefined function b(){ // 2. The upper scope of the b function is a. look up for scope console.log(scope) } return b; // 1. Although the declaration is after the return statement, it will still be promoted to the top level of the scope of the a function var scope = 'local scope' } a()() // undefined
1.1. Treatment of the same name
- Select the last declared function with the same name
- Variable and function have the same name, select function
var a = 1 var getNum = function() { a = 2 } function getNum() { a = 3 } getNum() console.log(a) // 2 // Select the promotion function with the same name as the variable and function. The function promotion includes initialization and assignment, and then execute the function. getNum declared by var is assigned as a function. After execution, change the variable a to 2
1.2. Lifting stage
establish | initialization | assignment | |
---|---|---|---|
let | promote | x | x |
var | promote | promote | x |
function | promote | promote | promote |
In the block scope, the variables declared by the let are promoted only when they are created. If the variables are used before initialization, a temporary dead zone will be formed
var name = 'World' ;(function () { if (typeof name === 'undefined') { var name = "HuaMu"; console.info('Goodbye ' + name) } else { console.info('Hello ' + name) } })() // If the name in the if branch will be promoted to the outer layer and has the same name as the global variable, the outer layer name cannot be accessed. var is only created and initialized without assignment, and the value is undefined, which meets the if condition // Goodbye HuaMu
2. Call stack
After the execution context is created, the JS engine will push the execution context into the stack. Generally, this stack used to manage the execution context is called execution context, also known as call stack
1. Call function
- The JS engine creates an execution context for the function and pushes it into the call stack
- JS engine executes function code
- After execution, the JS engine pops the execution context of the function out of the stack
2.2 stack overflow
When the allocated call stack space is full, a "stack overflow" problem will be caused. That is, the maximum stack capacity or maximum call depth has been exceeded
2.2.1 scenario
<!-- todo -->
3. Scope
Scope is the accessible scope of variables and functions, that is, scope controls the visibility and life cycle of variables and functions. It mainly includes global scope, function scope and block level scope
- When the current scope and the upper scope have variables with the same name, the upper variables cannot be accessed and affected
let a = 1 function b(a) { a = 2 console.log(a) } b(a) // 2 console.log(a) // 1
4. Scope chain
The chain that finds variables through the scope is called the scope chain, and the scope chain is determined through the lexical scope. The lexical scope is determined by the position of the function declaration. It is static, that is, it is determined in the code stage, and has nothing to do with how the function is called
// Operations such as concatenation are performed from right to left, which is equivalent to b = 10 and let a = b. It is equivalent to implicitly creating a global variable b let a = b = 10; ;(function(){ // Follow the scope chain to find the global variable b and modify it to 20 // Since it is redeclared, the a variable is only a local variable and does not affect the global variable a let a = b = 20 })() console.log(a) // 10 console.log(b) // 20
The function will only be compiled at the first execution, so the top-level data of variable environment and lexical environment have been determined at compilation time
var i = 1 function b() { console.log(i) } function a() { var i = 2 b() } // Since a function is defined in the global scope, even if b function is executed in a function, it can only access the global scope a() // 1
5. Closure
A scope refers to a scope that should have been destroyed, which is called a closure. That is, a function refers to the variable of the parent scope and is still called after the execution of the parent function
6,this
this is a function execution context object and a dynamically changing value. It has no scope restrictions. this in nested functions will not inherit from outer functions and can be processed through arrow functions and self
6.1 type
- this: window in global execution context
- this in the context of function execution: strict mode? undefined: window
var v_out = 'v_out'; let c_out = 'c_out'; var inner = { v_out: 'v_in', c_out: 'c_in', v_func: function () { return this.v_out }, c_func: function () { return this.c_out }, func:()=>{ return this.v_out } }; // Get the function within the scope of the object, call this to point to window in the global environment const v_method = inner.v_func; const c_method = inner.c_func; // Top layer V_ The out variable will be promoted and mounted to the window v_method(); // 'v_out' // Within the block scope, the variables declared by const will not be mounted to the window, and the parent scope cannot access the child scope c_method(); // undefined // Assignment expressions and comma expressions return the last value itself, inner v_ Func function itself is called in the global environment (inner.v_func, inner.v_func)(); // 'v_out' (inner.v_func = inner.v_func)(); // 'v_out' // Object, and this points to the object inner.v_func() // 'v_in' (inner.v_func)() // 'v_in' // The arrow function does not have its own execution context. It inherits this of the calling function, here is window inner.func() // 'v_out'
6.2. Change this direction
Binding priority: New > display binding (call, apply, bind) > implicit binding (call function object) > default binding (window)
6.2.1. Set through the call, apply and bind methods of the function
c_method.call(inner) c_method.apply(inner) c_method.bind(inner)()
Simple implementation: call, apply
- Link the current function to the specified context, that is, set the function as an object property
- The current function is executed in the context of context
- Remove the current function that has been executed in the context
/** * Simple implementation of apply * @param {Function} fn Currently running function * @param {Object} context Specified context * @param {Array} args Parameter set * @returns */ const apply = (fn,context=window,args=[])=>{ // Symbol is the sixth basic type added by es6. For object attributes, it is uuid const id = Symbol(); // Links the current function to the specified context context[id] = fn; // The current function is executed in the context of context const res = context[id](...args) // Remove the current function that has been executed in the context delete context[id] return res; } // -------test------- // const context = { value:1 } function fn (name,isGirl){ console.log("π ~ ", name,isGirl,this.value) } apply(fn,context,['huamu',true]) // π ~ huamu true 1
Simple implementation: bind
- Unlike call and apply, bind returns a new function
function bind (fn, context=window, ...args) { return (...args2)=> apply(fn,context,[...args,...args2]) }
6.2.2. By calling the function object: point to the object itself
The binding of this is where the function actually executes
6.2.3. Pass constructor
function Foo() { getName = function () { console.log(1) } return this } Foo.getName = function () { console.log(2) } Foo.prototype.getName = function () { console.log(3) } var getName = function () { console.log(4) } function getName() { console.log(5) } // Static method for executing Foo function Foo.getName() // 2 // The function getName is promoted and assigned, and the getName function expression is executed getName() // 4 // Execute the Foo function in the global environment, this points to window, and execute the getName method in the function, which overrides the getName in the global environment Foo().getName() // 1 getName() // 1 // New is used to call the function, i.e. new Foo Getname () is equivalent to new (Foo.getName)(), which executes the static method of Foo function new Foo.getName() // 2 // New and From left to right, which is equivalent to (New foo()) getName(), new will create a new object and execute the getName method of the new object. The method cannot be found in the new object itself, so find it from the prototype new Foo().getName() // 3 new new Foo().getName()
Simple implementation: new
- Create a new object and point to the function prototype
- Bind this to a new object
- Return object
const newFn = (fn, ...arg) => { const obj = Object.create(fn.prototype); fn.apply(obj,arg) return obj }
Extension: object The Create method creates an object and the__ proto__ Property points to the incoming object
const person = { address: { country:"china" }, number: 111, say: function () { console.log(`it's ${this.name}, from ${this.address.country}, nums ${this.number}`) }, setCountry:function (country,number) { this.address.country=country this.number = number } } // 1. The prototype objects of p1 and p2 point to the same object const p1 = Object.create(person) const p2 = Object.create(person) // 2. Add attribute p1.name = "huahua" // 3. Find the setCountry function on the prototype, and find the reference value address and the original value num be attribute. The reference value will be shared among all instances p1.setCountry("nanji",666) p2.name = "mumu" // 4. The modified value of p2 will overwrite that of p1, and the final value of country is beiji p2.setCountry("beiji",999) p1.say() // it's huahua, from beiji, nums 666 p2.say() // it's mumu, from beiji, nums 999
6.2.4 examples
const value = 1; const objContext = { value : 2, getThis:function() { // 'this' in nested functions will not inherit from outer functions function fn1() { console.log("π ~ fn1",this.value) // undefined } fn1() // Save this as a self variable, and then use the scope mechanism of the variable to pass it to the nested function const self = this; function fn2() { console.log("π ~ fn2 self",self.value) // 2 } fn2() // The arrow function does not have its own execution context and will inherit this in the calling function const fn3 = () => { console.log("π ~ fn3 Arrow function",this.value) // 2 } fn3() // The arrow function will not bind local variables, and all references involving them will be handled by looking up the outer scope chain. Therefore, this is bound only once function fn4() { return () => { return () => { return () => { console.log("π ~ fn4 Arrow function",this.value) // 42 }; }; }; } fn4.call( { value: 42 } )()()() // Constructor has the highest priority function fn5(value) { this.value = value } const fn = new fn5(100) console.log("π ~ fn5 Constructor", fn.value, this.value) // 100 2 } }
7. Prototype
7.1. Function object & Common Object
Objects created through new Function are called function objects, while others are ordinary objects. The constructor of ordinary objects is Object
// Function object function fn(){}; const fn = () =>{}; const fn = new Function('str') // Common object const obj = {} const obj = new Object() const obj = new fn()
Each object has a built-in__ proto__ Property, pointing to the prototype object of the constructor that created it, but only the function object has the prototype property, pointing to the prototype object of the function
7.2 prototype object
Each prototype object has a constructor pointer by default, pointing to the function where the prototype attribute is located
7.2.1,person1.__proto__ What is it?
Because: person1 The constructor for is Person So: person1.__proto__ === Person.prototype
7.2.2,Person.__proto__ What is it?
Because: Person The constructor for is Function So: Person.__proto__ === Function.prototype
7.2.3,Person.prototype.__proto__ What is it?
Because: Person.prototype Is an instance of a constructor, is an ordinary object, and its constructor is Object So: Person.prototype.__proto__ === Object.prototype Function.prototype.__proto__ === Object.prototype
7.2.4,Object.__proto__ What is it?
Because: all function objects__proto__All point Function.prototypeοΌIt is an empty function So: Object.__proto__ === Function.prototype
7.2.5,Object.prototype.__proto__ What is it?
Because: Object.prototype At the top of the prototype chain null So: Object.prototype.__proto__ === null
7.2.6. 12 JS built-in constructor objects
Eight accessible constructors point to function with Object prototype
Number,Boolean,String,Function,Array,RegExp,Error,Date
Two exist in the form of objects, and their proto points to object prototype
Math,JSON
1 Global that cannot be accessed directly, 1 Arguments created by JS engine only during function call
8. Inherit
- Prototype inheritance
- Constructor inheritance
- Combinatorial inheritance
- class inheritance: mainly rely on extensions and super (let the JavaScript engine realize the prototype chain code that we originally need to write ourselves)
9. Module
9.1 significance of modularization
If modularization is not adopted, the correct import order must be ensured when importing js files, otherwise it cannot run. In the case of large number of files and unclear dependencies, it is difficult to guarantee, so modularization appears
9.2,CommonJS & AMD
Prior to ES6, JS had no module system. Only some module loading schemes specified by the community, such as CommonJS for server-side synchronous loading and AMD and CMD for browser-side asynchronous loading
9.2.1,CommonJS(Node.js)
A file is a module with its own scope, which only exposes specific variables and functions. It has four important variables: module, exports, require and global
Exports itself is a variable object, pointing to module The {} module of exports can only be passed Syntax exposes variables. Module Exports can be passed You can also use = assignment, where exports is the attribute of module and points to {} module
//Write the functions and variables that need to be exposed module.exports = { add, update } // Reference to custom module must be added/ Path. If it is not added, it will only go to node_modules file not found var math = require('./math') // When referring to the core module, there is no need to bring a path var http = require('http')
9.2.2,AMD(require.js),CMD(sea.js)
Although js files are loaded in parallel, AMD advocates relying on pre loading and early execution, that is, pre loading, and CMD advocates relying on nearby and delayed execution, that is, lazy loading. It has three important variables: require. Which specifies the reference path Config, define the definde of the module and the require for loading the module
/** AMD Writing method**/ define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { // All modules to be used are declared and initialized at the beginning a.doSomething(); if (false) { // Even if a module b is not used, it is executed in advance b.doSomething() } }); /** CMD Writing method**/ define(function(require, exports, module) { //State when necessary var a = require('./a'); a.doSomething(); if (false) { var b = require('./b'); b.doSomething(); } });
9.3,ESM
CommonJS and AMD output objects. When they are introduced, they need to find the object attributes. They can only determine the dependency of the module and the input and output variables at run time, that is, load at run time. The design idea of ES6 module is to be static as much as possible. It exports not an object, but an interface, so that the dependency of the module and the input and output variables can be determined at compile time, Static loading
9.3.1 principle analysis
// index.js import { m } from './module'; // module.js const m = 1; const n = 2; export { m, n };
Package the above source code
// 1. Is an immediate execution function (function (modules) { var installedModules = {}; // 4. Processing entry file module function __webpack_require__(moduleId) { if (installedModules[moduleId]) { return installedModules[moduleId].exports; } // 5. Create a module var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} }; // 6. Execute the entry file module function modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); module.l = true; // 7. Return return module.exports; } __webpack_require__.d = function (exports, name, getter) { if (!__webpack_require__.o(exports, name)) { // Determine whether name is an attribute of exports Object.defineProperty(exports, name, {enumerable: true, get: getter}); } }; __webpack_require__.r = function (exports) { if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { // Symbol.toStringTag is the attribute of the object, and the value represents the custom type [Object Module] of the object // Usually only as object prototype. Return value of tostring() Object.defineProperty(exports, Symbol.toStringTag, {value: 'Module'}); } Object.defineProperty(exports, '__esModule', {value: true}); }; __webpack_require__.o = function (object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; // 3. Import file id return __webpack_require__(__webpack_require__.s = "./index.js"); })( // 2. The module object is passed in as a parameter { "./index.js": (function (module, __webpack_exports__, __webpack_require__) { // __ webpack_exports__ Module exports "use strict"; // Added__ esModule and symbol Tostringtag property __webpack_require__.r(__webpack_exports__); var _module__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./module.js"); }), "./module.js": (function (module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); // Add m/n these variables to module Exports, and set getter to the direct return value __webpack_require__.d(__webpack_exports__, "m", function () {return m;}); __webpack_require__.d(__webpack_exports__, "n", function () {return n;}); var m = 1; var n = 2; }) });
- Pass the module object into a function that executes immediately
- Pass in the file id and execute__ webpack_require__ function
- Create a module and bind this to the module Exports, and pass in module and module at the same time Exports object
- __ webpack_require__.r to module Add a symbol to the exports object Tostringtag property, the value is {value: 'Module'}, making module Exports calls toString to return [Object Module] representing a module
- __ webpack_require__.d add the variable to be exported to module Exports, set the getter to return the value of the variable with the same name, so that the variable changes and the external reference will also change
- Return module exports
Note: when ESM encounters the load command import, it only generates a dynamic read-only reference and takes values in the module when it needs to be called
9.4 difference between require and import
- CommonJS modular scheme require/exports is designed for server-side development. The server module system reads the contents of the module file synchronously, and obtains the module interface after compilation and execution. The ES6 modularization scheme import/export is designed for the browser, and the browser module system loads script files asynchronously
- require/exports is dynamic loading at runtime, and import/export is static compilation
- The output of require/exports is a copy of the value. The output of import/export module is the reference of the value, that is, if the module value referenced by the file changes, the module value introduced by require will not change, but the module value introduced by import will change
- Different usage
- ES6 module can be used before import reference statement, while CommonJS needs to be referenced before use
- import/export can only be used at the top level of the module and cannot be referenced in code blocks such as functions and judgment statements, while require/exports can
- import/export adopts strict mode by default
// require/exports const fs = require('fs') exports.fs = fs module.exports = fs // import/export import fileSystem, {readFile} from 'fs' // You do not need to add {} for modules imported and exported by export default. You need to add {} for modules imported and exported by non export default
10,babel
parse the source code string, generate ast, change the modification of the code into the addition, deletion and modification of AST, and print it into the object code string after converting ast
10.1 API for babel plug-in development
10.1.1 parse stage
Use @ babel/parser to convert the source code into AST
require('@babel/parser').parse(source, { sourceType: 'module', // Parsing es module syntax plugins: ['jsx'], // Specify plug-ins such as jsx to parse the corresponding syntax });
10.1.2. transform stage
Use @ babel/traverse to traverse the AST, and call the visitor function to modify the AST, @ babel/types is used to create and judge AST nodes, and provides APIs such as isX and assertX. If you create in batch, you can use @ babel/template
require('@babel/traverse').default(ast, { // do something })
10.1.3 generate stage
Use @ babel/generate to print AST as object code string and generate sourcemap at the same time, @ Babel / code frame is used to print code position in case of error
const { code,map } = generator(ast, { sourceMaps: true })
11. Regular
11.1 basic grammar
- Matching pattern
// +The leading character must appear one time in a row in the target string /\d+/ // *~ 0 consecutive starts /\d*/ // ? ~ 0, 1 time /\d?/ // ^Locate the first character of the string and the last character of the $string /^\d$/ // () is a capture group, [] matches a single character, and the element relationship is or /([\s\S]*?)["']/ // "+ 'any character
- Match character
// \S matches white space characters (spaces, line breaks, indents, etc.) \ s vice versa /[\s\S]*/ // Match all // \W matches words ([A-Za-z0-9#]) \W opposite /[\w\W]*/ // Match all // \b not all matches are the positions of \ w /\bnice\b/ // A nice day - > A is the explicit position, and the position between a and N is the implicit position, that is, the position from a to n is the \ b matching position. Similarly, the position from e to d is also the \ b matching position, so it can be matched to nice /\b.\bnice\b/ // A nice day - > match to a nice
- Match number
/[0-9]/ /\d/ // Single number /[0-9]+/ /\d*/ // Multiple numbers /[\d]{1,3}/ // Specify the number, 1-3 numbers
11.2 example analysis
- Practical http routing expression
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/
Disassembly matching
http + (0|1 individual)s + :// +(0 | 1) www. + (1-256) -| a-z|A-Z|0-9 @ |: |% |. | + | ~ | | = ++ (1-6) a-z|A-Z|0-9 | (|) + not all matching is the position of \ w + (any) -| a-z|A-Z|0-9 | (|) | @ |: |% | | + |. | ~ | # ||&|/|=