JavaScript

The data type

String, Number, Boolean, Null, Undefined, Symbol, BigInt, Object

Heap, stack

Both are places to store data.

A stack is an automatically allocated memory space that holds values of primitive types and memory addresses of reference types.

The heap is a dynamically allocated memory space that holds values of reference types.

JavaScript does not allow direct manipulation of objects in the heap space. When manipulating objects, the actual operation is the reference of the object, and the memory address stored in the stack space serves as a point to find the value of the corresponding reference type in the heap space.

Implicit type conversion

As a weakly typed language, JavaScript automatically converts types in some scenarios due to its flexibility.

There are three common implicit type conversion scenarios: operation, negation, and comparison

operation

The implicit type conversion of an operation converts the operation’s member to type number.

Primitive type conversion:

true + false   / / 1
null + 10      / / 10
false + 20     / / 20
undefined + 30 // NaN
1 + '2'        / / "12"
NaN + ' '       // "NaN"
undefined + ' ' // "undefined"
null + ' '      // "null"
' ' - 3         // -3
Copy the code
  • null,false,' 'conversionnumberThey’re all of type 0
  • undefinedconversionnumberType isNaN, soundefinedAnd other primitive type operationsNaN
  • Strings are strong in addition (actually string concatenation), and adding to any type produces a string (symbolExcept), even if it isNaN,undefined. Other operations are converted normallynumberI’m going to do it.

Reference type conversion:

[1] + 10    / / "110"
[] + 20     20 "/ /"
[1.2] + 20  // "1,220"
[20] - 10   / / 10
[1.2] - 10  // NaN
({}) + 10   // "[object Object]10"({})10   // NaN
Copy the code
  • Is called first when a reference type operation is performedvalueOfDetermines whether the returned value is the original data type and performs the operation if so. If not, calltoStringTo convertstringOperation again
  • Same conclusion, except for the addition of the output string, the other cases are firststringTurn againnumber

Parse the reference type conversion process:

[1.2] + 20
/ / process:
[1.2].toString() / / '1, 2,'
'1, 2,' + 20       // '1,220'

[20] - 10
/ / process
[20].toString()  / / '20'
Number('20')     / / 20
20 - 10          / / 10
Copy the code

The not

The inverted implicit type conversion converts the operation’s member to a Boolean type.

The implicit type conversion is relatively simple, which is to convert a value to a Boolean value and invert it:

! []// false! {}// false
!false  // true
Copy the code

Usually to quickly get the Boolean type of a value, we can invert it twice:

!!!!! []// true!!!!!0   // false
Copy the code

To compare

Comparison is divided into strict comparison === and non-strict comparison ==, because === will compare types, no type conversion. I’m only talking about theta = theta.

The implicit type conversion of the comparison basically converts the member of the operation to type number.

undefined= =null  // true
' '= =0            // true
true= =1          // true
'1'= =true        // true
[1] = ='1'         // true
[1.2] = ='1, 2,'     // true= = ({})'[object Object]' // true
Copy the code
  • undefinedIs equal to thenull
  • String, Boolean,nullWhen they compare, they all turnnumber
  • Reference types are cast to implicitlystringCompare, if not equal then convertnumberTo compare

precompiled

Precompilation occurs before JavaScript code is executed, parsing and generating the code, initializing the creation and storing variables, and preparing the code for execution.

Precompilation process:

  1. Create GO/AO objects (GO is global, AO is active)
  2. Assign the parameter and variable declaration toundefined
  3. Arguments and parameters are unified
  4. Function declaration promotion (assigning variables to function bodies)

Example:

function foo(x, y) {
    console.log(x)
    var x = 10
    console.log(x)
    function x(){}
    console.log(x)
}
foo(20.30)
Copy the code
// 1. Create AO objects
AO {}
// 2. Find the parameter and variable declaration assigned to undefined
AO {
    x: undefined
    y: undefined
}
// 3. The arguments are consistent with the parameters
AO {
    x: 20
    y: 30
}
// 4. Function declaration is improved
AO {
    x: function x(){}
    y: 30
}
Copy the code

The first x is evaluated from the AO, and the output is function x; X is assigned 10, and the second x prints 10; The function x has been declared promoted, there is no further assignment of x, and the third x prints 10.

scope

A scope guarantees orderly access to all variables and functions that it has access to, and is a rule for code to look up variables at run time.

Function scope

Functions create their own scope at run time, “hiding” internal variables and function definitions, and the outer scope cannot access anything inside the wrapper function.

Block-level scope

Block-level scopes were created prior to ES6 using either with or try/catch. Block-level scope declarations are made easier with the introduction of the let keyword in ES6. The let keyword binds variables to any scope they are in (usually {… } inside).

{
    let num = 10
}
console.log(num) // ReferenceError: num is not defined
Copy the code

Parameter scope

Once the default values of parameters are set, the parameters form a separate scope when the function is declared initialized. After initialization, the scope will disappear. This syntactic behavior does not occur if the parameter defaults are not set.

let x = 1;

function f(x, y = x) {
  console.log(y);
}

f(2) / / 2
Copy the code

The default value of argument y is equal to variable x. When f is called, the arguments form a separate scope. In this scope, the default variable x refers to the first parameter x, not the global variable x, so the output is 2.

let x = 1;
function foo(x, y = function() { x = 2; }) {
  x = 3;
  y();
  console.log(x);
}

foo() / / 2
x / / 1
Copy the code

Y defaults to an anonymous function where x points to the first argument x of the same scope. The internal variable x of the function foo refers to the first parameter x, which is the same as the internal x of the anonymous function. The y function reassigns the value to x and outputs 2, while the outer global variable x remains intact.

closure

The nature of closures is a scoping problem. Closures occur when a function can remember and access its scope, and the function is called outside its scope.

Simply put, a function refers to a variable in its scope, and it is saved for execution in other scopes. The scope of the reference variable does not disappear, but follows the function. When this function is executed, variables can be found through the scope chain.

let bar
function foo() {
    let a = 10
    // The function is saved externally
    bar = function () {
        // reference variable A that is not currently scoped
        console.log(a)
    }
}
foo()
The bar function is not executed in its own scope
bar() / / 10
Copy the code

Advantages: Private variables or methods, caching

Disadvantages: Closures do not release scoped chains, which can lead to memory leaks

Prototype chain

Objects in JavaScript have a special built-in property, prototype, which is a reference to other objects. When a variable is searched, it will first look for its own Object. If it cannot find a variable, it will look for the Object’s prototype, and so on, and finally Object. Prototype. Multiple prototypes linked together are called prototype chains.

Prototype inheritance

There are many methods of stereotype inheritance, and I won’t cover all of them here, but only two commonly used methods.

The holy grail mode

function inherit(Target, Origin){
  function F() {};
  F.prototype = Origin.prototype;
  Target.prototype = new F();
  / / reduction constuctor
  Target.prototype.constuctor = Target;
  // From whom records are inherited
  Target.prototype.uber = Origin.prototype; 
}
Copy the code

The benefit of the Holy Grail mode is that with intermediate object isolation, any attributes added by children are added to the object without affecting the parent. And the search attribute is along __proto__ search, can smoothly find the parent level of the attribute, inheritance.

Use:

function Person() {
    this.name = 'people'
}
Person.prototype.sayName = function () { console.log(this.name) }
function Child() {
    this.name = 'child'
}
inherit(Child, Person)
Child.prototype.age = 18
let child = new Child()
Copy the code

ES6 Class

class Person {
    constructor() {
        this.name = 'people'
    }
    sayName() {
        console.log(this.name)
    }
}
class Child extends Person {
    constructor() {
        super(a)this.name = 'child'
    }
}
Child.prototype.age = 18
let child = new Child()
Copy the code

Classes can be inherited through the extends keyword, which is much cleaner and more convenient than ES5’s inheritance through modified prototype chains.

Basic packing type

let str = 'hello'
str.split(' ')
Copy the code

Primitive types have no properties or methods, but in practice we can call methods from primitive types, just as a string can call a split method.

To facilitate manipulation of primitive type values, each time a primitive type value is read, an object of the corresponding primitive wrapper type is created behind the scenes, allowing us to call methods to manipulate the data. The general process is as follows:

  1. createStringType instance
  2. Invokes the specified method on the instance
  3. Destroy the instance
let str = new String('hello')
str.split(' ')
str = null
Copy the code

this

This is the binding that occurs when the function is called, and what it points to depends entirely on where the function is called. When you use this inside a function, you can access properties and methods on the caller object.

There are four cases of the this binding:

  1. The new binding.newinstantiation
  2. Show bindings.call,apply,bindManually change pointing
  3. Implicit binding. Called by a context object, as inobj.fn().thisPoint to theobj
  4. Default binding. Global objects are bound by default, and in strict mode toundefined

The new binding has the highest priority and is last to the default binding.

The process of new

  1. Create an empty object
  2. Set the prototype to the object__proto__Object pointing to the constructorprototype
  3. ConstructorthisExecutes the object and executes the constructor to add properties and methods to the empty object
  4. Return instance object

If the basic type is returned, the construction process is terminated prematurely and the instance object is returned. If a reference type is returned, that reference type is returned.

// Return the base type
function Foo(){
    this.name = 'Joe'
    return 123
    this.age = 20
}
new Foo() // Foo {name: "Joe"}

// Return the reference type
function Foo(){
    this.name = 'Joe'
    return [123]
    this.age = 20
}
new Foo() / / [123]
Copy the code

Call, apply, bind

All three of them change the direction of this.

Call and apply change this to point to and call a function. The difference between call and apply is that they pass arguments one by one, while call and apply pass a list of arguments of array type.

Bind changes this and returns a function reference. Multiple calls to bind are invalid, and the this reference it changes only takes the first call.

Write a call

Function.prototype.mycall = function () {
  if(typeof this! = ='function') {throw 'caller must be a function'
  }
  let othis = arguments[0] | |window
  othis._fn = this
  let arg = [...arguments].slice(1)
  letres = othis._fn(... arg)Reflect.deleteProperty(othis, '_fn') // Delete the _fn attribute
  return res
}
Copy the code

For the apply implementation, modify the parameter format

Write a bind

Function.prototype.mybind = function (oThis) {
  if(typeof this! ='function') {throw 'caller must be a function'
  }
  let fThis = this
  / / Array. Prototype. Slice. Call the class Array into an Array
  let arg = Array.prototype.slice.call(arguments.1)
  let NOP = function(){}
  let fBound = function(){
    let arg_ = Array.prototype.slice.call(arguments)
    // The new binding level is higher than the explicit binding
    When called as a constructor, the pointer is left unchanged
    // Use instanceof to determine if it is a constructor call
    return fThis.apply(this instanceof fBound ? this : oThis, arg.concat(arg_))
  }
  // Maintain the prototype
  if(this.prototype){
    NOP.prototype = this.prototype
    fBound.prototype = new NOP()
  }
  return fBound
}
Copy the code

Knowledge of ES6 syntax

Common: let, const, extension operator, template string, object destruct, arrow function, default argument, Promise

Data structure: Set, Map, Symbol

Others: Proxy and Reflect

Set, Map, WeakSet, WeakMap

Set:

  • The values of the members are unique and have no duplicate values, similar to an array
  • Can traverse

WeakSet:

  • The member must be of reference type
  • Members are weak references and can be garbage collected. After the external reference to which a member points is reclaimed, the member can also be reclaimed
  • Can’t traverse

Map:

  • A collection of key-value pairs that can be of any type
  • Can traverse

WeakMap:

  • Only the reference type is accepted as the key name
  • The key name is a weak reference, and the key value can be any value and can be garbage collected. If the external reference to the key name is reclaimed, the corresponding key name can also be reclaimed
  • Can’t traverse

The difference between an arrow function and a normal function

  1. Arrow functionthisThe point is determined at code writing, that is, the scope in which the arrow function itself resides; Normal functions are determined at call timethis.
  2. Arrow function doesn’t have anyarguments
  3. Arrow function doesn’t have anyprototypeattribute

Promise

Promise is a new asynchronous programming solution in ES6 that avoids the callback hell problem. The Promise object is realized by changing the state. The asynchronous operation is represented by the synchronous process. As long as the state changes, the corresponding function will be automatically triggered.

Promise objects have three states, which are:

  • pending:Default, as long as it is not toldpromiseThe mission is a success or a failurependingstate
  • fulfilled:As long as the callresolveDelta function, the state is going to be zerofulfilled“Indicates that the operation succeeds
  • rejected:As long as the callrejectedDelta function, the state is going to be zerorejected“Indicates that the operation fails

Once a state change is irreversible, a function can be used to monitor the change of the Promise state. A callback to the then function is executed on success, and a callback to the catch function is executed on failure

Shallow copy

A shallow copy is a copy of a value. For a copy of an object that is a memory address, the reference to the target object and the reference to the source object point to the same memory space. If one object changes, it affects the other.

Common shallow copy methods:

  • Array.prototype.slice
let arr = [{a:1}, {b:2}]
let newArr = arr1.slice()
Copy the code
  • Extended operator
let newArr = [...arr1]
Copy the code

Deep copy

A deep copy is a complete copy of an object from the memory. Instead of sharing the memory with the object, a new space is created in the heap memory to store the object. Therefore, modifying the new object does not affect the original object.

Common deep copy methods:

  • JSON.parse(JSON.stringify())
JSON.parse(JSON.stringify(obj))
Copy the code
  • Handwritten deep copy
function deepClone(obj, map = new WeakMap(a)) {
  if (obj === null || typeofobj ! = ="object") return obj; 
  const type = Object.prototype.toString.call(obj).slice(8, -1) 
  let strategy = {
    Date: (obj) = > new Date(obj),
    RegExp: (obj) = > new RegExp(obj),
    Array: clone,
    Object: clone
  }
  function clone(obj){
    // Prevent circular references, resulting in stack overflow, the same reference object directly returned
    if (map.get(obj)) return map.get(obj);
    let target = new obj.constructor();
    map.set(obj, target);
    for (let key in obj) {
      if(obj.hasOwnProperty(key)) { target[key] = deepClone(obj[key], map); }}return target;
  }
  return strategy[type] && strategy[type](obj)
}
Copy the code

Event delegation

Event delegation, also known as event broker, is a means of DOM event optimization. Event delegates manage all events of a certain type by specifying only one event handler using the event bubbling mechanism.

Suppose you have a list where each child element has a click event. Event delegates can be used to optimize scenarios where the memory footprint of event bindings increases linearly as the number of child elements increases. Broker events are usually bound to parent elements rather than having to add events to each child element.

<ul @click="clickHandler">
    <li class="item">1</li>
    <li class="item">2</li>
    <li class="item">3</li>
</ul>
Copy the code
clickHandler(e) {
    // Click on the child element
    let target = e.target
    // Outputs the child element content
    consoel.log(target.textContent)
}
Copy the code

Image stabilization

Anti-shake is used to reduce the number of function calls, and for frequent calls, only the last of those calls is executed.

/ * * *@param {function} func- Executes the function *@param {number} wait- Waiting time *@param {boolean} immediate- Whether to execute * immediately@return {function}* /
function debounce(func, wait = 300, immediate = false){
  let timer, ctx;
  let later = (arg) = > setTimeout(() = >{
    func.apply(ctx, arg)
    timer = ctx = null
  }, wait)
  return function(. arg){
    if(! timer){ timer = later(arg) ctx =this
      if(immediate){
        func.apply(ctx, arg)
      }
    }else{
      clearTimeout(timer)
      timer = later(arg)
    }
  }
}
Copy the code

The throttle

Throttling is used to reduce the number of function requests, and unlike stabilization, throttling is performed once in a period of time.

/ * * *@param {function} func- Executes the function *@param {number} delay- Delay time *@return {function}* /
function throttle(func, delay){
  let timer = null
  return function(. arg){
    if(! timer){ timer =setTimeout(() = >{
        func.apply(this, arg)
        timer = null
      }, delay)
    }
  }
}
Copy the code

Currie,

Currying is a technique that converts a function that takes multiple arguments into a function that takes a single argument, and returns a new function that takes the remaining arguments and returns a result.

General Coriolization functions:

function currying(fn, arr = []) {
  let len = fn.length
  return (. args) = > {
    let concatArgs = [...arr, ...args]
    if (concatArgs.length < len) {
      return currying(fn, concatArgs)
    } else {
      return fn.call(this. concatArgs) } } }Copy the code

Use:

let sum = (a,b,c,d) = > {
  console.log(a,b,c,d)
}

let newSum = currying(sum)

newSum(1) (2) (3) (4)
Copy the code

Advantages:

  1. Parameter multiplexing, since the parameters can be passed separately, we can reuse the function after passing the parameters
  2. Delay execution, just likebindYou can also receive arguments and return a reference to a function without calling it

The garbage collection

The heap is divided into the new generation and the old generation, respectively by the secondary garbage collector and the main garbage collector to be responsible for garbage collection.

The new generation

Generally, the newly used objects will be placed in the New generation, which is relatively small, only dozens of MB, and the new generation will be divided into two Spaces: form space and to space.

Objects are first allocated to the form space, and in the garbage collection phase, the surviving objects in the form space are copied to the TO space, and the surviving objects are recycled, and then the two Spaces are swapped. This algorithm is called “Scanvage”.

The new generation of memory reclamation is very frequent and fast, but space utilization is low because half of the memory space is left “idle”.

The old generation

The old generation has a large space, and the objects that are still alive after multiple recycling of the new generation will be sent to the old generation.

The old generation used a “tag sweep” approach to mark surviving objects by traversing them from the root element. When the marking is complete, unmarked objects are reclaimed.

The memory space cleared by the mark will create a lot of discontinuous fragmentation space, causing some large objects to be unable to fit in. So after the recycling is done, these discontinuous debris Spaces are sorted out.

JavaScript Design pattern

The singleton pattern

Definition: Ensures that a class has only one instance and provides a global access point to access it.

As a classless language, the traditional singleton pattern concept is not applicable in JavaScript. Thinking a little differently: The singleton pattern ensures that there is only one object and provides global access.

The common application scenario is the popover component, using the singleton mode to encapsulate the global popover component method:

import Vue from 'vue'
import Index from './index.vue'

let alertInstance = null
let alertConstructor = Vue.extend(Index)

let init = (options) = >{
  alertInstance = new alertConstructor()
  Object.assign(alertInstance, options)
  alertInstance.$mount()
  document.body.appendChild(alertInstance.$el)
}

let caller = (options) = >{
  // singleton judgment
  if(! alertInstance){ init(options) }return alertInstance.show(() = >alertInstance = null)}export default {
  install(vue){
    vue.prototype.$alert = caller
  }
}
Copy the code

The component is instantiated only once, no matter how many times it is called, and you end up getting the same instance.

The strategy pattern

Definition: Define a set of algorithms, encapsulate them one by one, and make them interchangeable.

The policy pattern is the most commonly used design pattern in development, and in some scenarios where there are a large number of if/else and each branch point is functionally independent, the policy pattern can be considered for optimization.

For example, the strategy mode is used for deep copy:

function deepClone(obj, map = new WeakMap(a)) {
  if (obj === null || typeofobj ! = ="object") return obj; 
  const type = Object.prototype.toString.call(obj).slice(8, -1) 
  // Policy object
  let strategy = {
    Date: (obj) = > new Date(obj),
    RegExp: (obj) = > new RegExp(obj),
    Array: clone,
    Object: clone
  }
  function clone(obj){
    // Prevent circular references, resulting in stack overflow, the same reference object directly returned
    if (map.get(obj)) return map.get(obj);
    let target = new obj.constructor();
    map.set(obj, target);
    for (let key in obj) {
      if(obj.hasOwnProperty(key)) { target[key] = deepClone(obj[key], map); }}return target;
  }
  return strategy[type] && strategy[type](obj)
}
Copy the code

This code looks much cleaner, maintaining only one policy object and adding a policy when new functionality is needed. Because policy items are individually encapsulated methods, they are also easier to reuse.

The proxy pattern

Definition: Provide a proxy for an object to control access to it.

When it is inconvenient to access an object directly or when the need is not met, a proxy object is provided to control the access to the object. It is the proxy object that actually accesses the object. The proxy object processes the request and then passes it to the ontology object.

Request data using caching proxy:

function getList(page) {
    return this.$api.getList({
        page
    }).then(res= > {
        this.list = res.data
        return res
    })
}

/ / agent getList
let proxyGetList = (function() {
    let cache = {}
    return async function(page) {
        if (cache[page]) {
            return cache[page]
        }
        let res = await getList.call(this, page)
        return cache[page] = res.data
    }
})()
Copy the code

The above scenario is a common paging requirement. The same page data needs to be fetched in the background only once, and the retrieved data is cached. The next time the same page is requested, the previous data can be used directly.

Publish and subscribe model

Definition: It defines a one-to-many dependency between objects, and all dependent objects are notified when an object’s state transmission changes.

The main advantage of the publish-subscribe model is to solve the decoupling between objects. It is widely used in asynchronous programming as well as to help us complete loosely coupled code writing. Communication methods like eventBus are publish-subscribe.

let event = {
    events: [].on(key, fn){
        if(!this.events[key]) {
            this.events[key] = []
        }
        this.events[key].push(fn)
    },
    emit(key, ... arg){
        let fns = this.events[key]
        if(! fns || fns.length ==0) {return false
        }
        fns.forEach(fn= > fn.apply(this, arg))
    }
}
Copy the code

The above is a simple implementation of the publish-subscribe pattern, to which you can also add the off method to cancel listening events. In a Vue, it is common to instantiate a new Vue instance to act as a publish and subscribe hub for component communication. In the small program, you can manually implement the publish and subscribe mode to solve the problem of page communication.

Decorator mode

Definition: To dynamically add additional responsibilities to an object without affecting the object itself.

The decorator pattern is also a popular design pattern in development, as it makes it easy to extend properties and methods without affecting the source code. For example, the following application scenario is submitting a form.

methods: {
    submit(){
        this.$api.submit({
            data: this.form
        })
    },
    // Add validation to submit forms
    validateForm(){
        if(this.form.name == ' ') {return
        }
        this.submit()
    }
}
Copy the code

Imagine if you have just taken on a project and the logic of submit is complicated and can involve many things. It’s risky to intrude into the source code to extend functionality, where the decorator pattern comes in handy.

modular

Only two commonly used modules are recorded here: CommonJS module and ES6 module.

CommonJS module

Node.js adopts the CommonJS module specification, which is loaded synchronously when it is run on the server and can be run only after compilation when it is used on the client.

The characteristics of

  • Modules can be loaded multiple times. However, the first time the module is loaded, the result is cached, and the module is loaded again to fetch the cached result directly
  • The order in which modules are loaded, in the order they appear in the code

grammar

  • Exposure module:module.exports = valueexports.xxx = value
  • Introduction module:require('xxx')If the module is a third-party module, XXX is the module name. For a custom module, XXX is the module file path
  • Clear module cache:delete require.cache[moduleName];, the cache is saved inrequire.cache, you can delete the property

Module loading mechanism

  • Loading a module is actually loading the modulemodule.exportsattribute
  • exportsIs pointing tomodule.exportsA reference to the
  • module.exportsIs an empty object,exportsIs also an empty object,module.exportsWhen the object is not emptyexportsThe object is ignored
  • A module loads a copy of a value, and once the value is printed, changes within the module do not affect the value, except for reference types

Module. exports is not empty:

// nums.js
exports.a = 1
module.exports = {
    b: 2
}
exports.c = 3
Copy the code
let nums = require('./nums.js') // { b: 2 }
Copy the code

The module. Exports is empty:

// nums.js
exports.a = 1
exports.c = 3
Copy the code
let nums = require('./nums.js') // { a: 1, c: 3 }
Copy the code

Value copy embodiment:

// nums.js
let obj = {
    count: 10
}
let count = 20
function addCount() {
    count++
}
function getCount() {
    return count
}
function addObjCount() {
    obj.count++
}
module.exports = { count, obj, addCount, getCount, addObjCount }
Copy the code
let { count, obj, addCount, getCount, addObjCount } = require('./nums.js')

// Primitive types are not affected
console.log(count) / / 20
addCount()
console.log(count) / / 20
// If you want to get the changed value, you can use the function return
console.log(getCount()) / / 21

// The reference type will be changed
console.log(obj) // { count: 10 }
addObjCount()
console.log(obj) // { count: 11 }
Copy the code

ES6 module

The ES6 module is designed to be as static as possible, so that the module dependencies, as well as the input and output variables, can be determined at compile time.

The characteristics of

  • For static analysis reasons, ES6 module loading can only be used at the top level of code
  • Modules cannot load the same variable more than once

grammar

  • Exposure module:exportexport default
  • Introduction module:import

Module loading mechanism

  • Modules load copies of references, and changes within modules affect values
// nums.js
export let count = 20
export function addCount() {
  count++
}
export default {
  other: 30
}
Copy the code
// Import both export default and export variables
import other, { count, addCount } from './async.js'

console.log(other) // { other: 30 }
console.log(count) / / 20
addCount()
console.log(count) / / 21
Copy the code

Differences between ES6 modules and CommonJS modules

  1. The CommonJS module prints a copy of the value, the ES6 module prints a reference to the value.
  2. The CommonJS module is run time loaded, and the ES6 module is compile time output interface.

The resources

  • JavaScript Advanced Programming (Version 3)
  • JavaScript You Don’t Know (Volume 1)
  • JavaScript Design Patterns and Development Practices
  • ES6 Tutorial
  • Front-end Modular Details (Full version)

Recommended reading

  • Gold nine silver ten, preliminary intermediate front-end interview review summary “Vue chapter”
  • Summary of “Browser, HTTP, Front-end security”