Other chapters:

  • HTML & CSS
  • Javascript Part 1
  • Javascript Part 2
  • Vue part
  • The React part
  • Network part
  • Part of the performance

22. What is event bubbling?

Event bubbling coincided with event capture, in contrast, the current element — — — – > the body — — — — — — — — > > HTML document — – > Windows. When an event occurs on a DOM element, it does not happen exactly on that element. In the bubble phase, the event bubbles, or the event occurs in its parent, grandparent, grandparent, until it reaches the window.

/ / HTML structure<div class="grandparent">
  <div class="parent">
    <div class="child">1</div>
  </div>
</div>

Copy the code
/ / JS code
function addEvent(el, event, callback, isCapture = false) {
  if(! el || ! event || ! callback ||typeofcallback ! = ='function') return;
  if (typeof el === 'string') {
    el = document.querySelector(el);
  };
  el.addEventListener(event, callback, isCapture);
}

addEvent(document.'DOMContentLoaded'.() = > {
  const child = document.querySelector('.child');
  const parent = document.querySelector('.parent');
  const grandparent = document.querySelector('.grandparent');

  addEvent(child, 'click'.function (e) {
    console.log('child');
  });

  addEvent(parent, 'click'.function (e) {
    console.log('parent');
  });

  addEvent(grandparent, 'click'.function (e) {
    console.log('grandparent');
  });

  addEvent(document.'click'.function (e) {
    console.log('document');
  });

  addEvent('html'.'click'.function (e) {
    console.log('html');
  })

  addEvent(window.'click'.function (e) {
    console.log('window'); })});Copy the code

The addEventListener method has a third optional argument, useCapture, which defaults to false and events occur during the bubble phase or, if true, during the capture phase. If you click on the Child element, it prints Child, parent, Grandparent, HTML, Document, and Window, respectively, on the console, which is the event bubble.

23. DOM manipulation – How to add, remove, move, copy, create, and find nodes?

(1) Create a node

  createDocumentFragment()    // Create a DOM fragment
  createElement()   Create a concrete element
  createTextNode()   // Create a text node

Copy the code

(2) Add, remove, replace, insert

appendChild(node)
removeChild(node)
replaceChild(new,old)
insertBefore(new,old)

Copy the code

(3) Search

getElementById();
getElementsByName();
getElementsByTagName();
getElementsByClassName();
querySelector();
querySelectorAll();

Copy the code

(4) Attribute operation

getAttribute(key);
setAttribute(key, value);
hasAttribute(key);
removeAttribute(key);

Copy the code

24. What native methods do arrays and strings have

Array methods

Array methods instructions
Array.push() This method adds a new element to the end of the array. This method changes the length of the array:
Array.pop() This method removes the last element after the array and returns the array. This method changes the length of the array:
Array.shift() This method removes the first element after the array and returns the array. This method changes the length of the array:
Array.unshift() This method adds one or more elements to the beginning of an array and returns the length of the new array:
Array.isArray() If an object is an array, it returns a Boolean value
Array.concat() This method is a method that can concatenate multiple arrays into one array
Array.map() This method calls one of the provided functions for each element in the array and returns a new array, unchanged from the original array
Array.forEach() This method executes each element in the array into the provided function, with no return value, and is distinguished from the map method
Array.filter() This method evaluates all elements and returns the elements that meet the criteria as a new array
Array.every() This method evaluates all elements and returns a Boolean value, true if all elements meet the criteria, false otherwise:
Array.some() This method evaluates all elements and returns a Boolean value, true if there are elements that meet the criteria, false if there are elements that do not meet the criteria:
Array.reduce() This method calls the return function for all elements and returns the final result. The value passed in must be of type function:
Array.toString() This method converts an array to a string:
Array.join() This method also converts an array to a string:
Array.splice() (start position, delete number, element) universal method, you can add, delete, change:

String method

dad instructions
charAt(num) Gets a single character at the specified index position
charCodeAt(num) Gets the Unicode value of the character at the specified index position (ASCII is its subset)
concat(str01,str02) Join two characters
indexOf(“str”) Take the index of the first occurrence of STR
lastIndexOf(“str”) Takes the index of the last occurrence of STR
replace( “oldStr” , “newStr” ) Replace oldStr with newStr
slice( start , end ) Its object can be a string or array, and remember that its scope does not include end
substr( start , length ) The length length cannot be less than 0 or an empty string is returned
search(“str”) Search str01 for the string “STR”, returning the index of its first character in STR01
String.fromCharCode(uniNum,uniNum) Unicode values (>=1)
str01.localeCompare(str02) If str01> STR02 = 1 and if -1 is equal to 0 according to local specific rules
toLowerCase() Converts the string to lowercase and returns a new string.
toUpperCase() Uppercase the string and return the new string.
substring() Retrieves the character that a string mediates between two specified subscripts
split() Splits a string into an array of strings
match() Returns an array of all searched keyword content

25. What is Ajax? How do I create an Ajax?

My understanding of Ajax is that it is a way to communicate asynchronously, by sending HTTP communication directly from a JAVASCRIPT script to the server, and then updating the corresponding part of the web page based on the data returned by the server without refreshing the entire page.

Creation steps:

Interview handwriting (native) :

//1: Create Ajax objects
var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');// Compatible with Internet Explorer 6 and later versions
//2: Configure the Ajax request address
xhr.open('get'.'index.xml'.true);
//3: sends a request
xhr.send(null); //
//4: listens for the request and receives the response
xhr.onreadysatechange=function(){
     if(xhr.readySate==4&&xhr.status==200 || xhr.status==304 )
          console.log(xhr.responsetXML)
}


Copy the code

JQuery writing

  $.ajax({
          type:'post'.url:' '.async:ture,//async async sync Synchronization
          data:data,// For post requests
          dataType:'jsonp'.success:function (msg) {},error:function (error) {}})Copy the code

Promise encapsulates the implementation:

// Promise encapsulates the implementation:

function getJSON(url) {
  // Create a Promise object
  let promise = new Promise(function(resolve, reject) {
    let xhr = new XMLHttpRequest();

    // Create an HTTP request
    xhr.open("GET", url, true);

    // Set the status listener
    xhr.onreadystatechange = function() {
      if (this.readyState ! = =4) return;

      // Change the state of the promise when the request succeeds or fails
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText)); }};// Set the error listener
    xhr.onerror = function() {
      reject(new Error(this.statusText));
    };

    // Set the data type of the response
    xhr.responseType = "json";

    // Set the request header information
    xhr.setRequestHeader("Accept"."application/json");

    // Send an HTTP request
    xhr.send(null);
  });

  return promise;
}

Copy the code

26. What are the ways of js lazy loading?

The loading, parsing, and execution of JS will block the rendering process of the page, so we want js scripts to load as lazily as possible to improve the rendering speed of the page.

Some of the ways I’ve learned are:

  1. Place the JS script at the bottom of the document so that the JS script is loaded and executed as late as possible.
  2. Add the defer attribute to the JS script, which will allow the script load to be parsed at the same time as the document is parsed, and then execute the script file after the document has been parsed, so that the rendering of the page does not block. Several scripts that have the defer property set end up executing sequentially by specification, but this may not be the case in some browsers.
  3. Add an async property to the JAVASCRIPT script. This property causes the script to load asynchronously and does not block the page parsing process. However, executing the JAVASCRIPT script immediately after loading will block if the document is not parsed. Scripts with multiple async properties execute in an unpredictable order and are generally not executed in code order.
  4. In the way of dynamically creating DOM tags, we can monitor the loading event of the document, and then dynamically create script tags to introduce JS scripts after the document is loaded.

27. What is your understanding of modular development?

My understanding of a module is that a module is a set of methods that implement a particular function. In the beginning, JS only realized some simple functions, so there was no concept of module, but as the program became more and more complex, the modular development of code became more and more important.

Since functions have independent scope, the most primitive writing method is to use functions as modules, and several functions as a module, but this way is easy to cause global variables contamination, and there is no connection between modules.

Object writing is proposed later, which addresses some of the drawbacks of using functions directly as modules by implementing functions as an object method, but this approach exposes all module members, and external code can modify the values of internal attributes.

The most common approach is to write functions immediately, using closures to establish a module’s private scope without contaminating the global scope.

29. How many module specifications of JS?

Currently, there are four mature module loading schemes in JS:

  • The first is the CommonJS scheme, which imports modules via require and defines the module’s output interface via module.exports. This module loading solution is a server-side solution that introduces modules in a synchronous manner. Since files on the server side are stored on local disk, it is very fast to read, so it is no problem to load modules in a synchronous manner. However, on the browser side, asynchronous loading is more appropriate because the module is loaded using a network request.
  • The second is THE AMD scheme, which adopts the asynchronous loading mode to load the module. The loading of the module does not affect the execution of the following statements. All statements that depend on the module are defined in a callback function, and the callback function is executed after the loading is complete. Require.js implements the AMD specification.
  • The third is CMD scheme, this scheme and AMD scheme are to solve the problem of asynchronous module loading, sea. Js implementation of CMD specification. It differs from require.js in how dependencies are handled in module definition and when dependent modules are executed.
  • The fourth solution is proposed in ES6, which uses the form of import and export to import and export modules.

30. Talk about how JS works

1. Js single thread

One of the hallmarks of the JavaScript language is single-threaded, which means you can only do one thing at a time.

The single thread of JavaScript, relative to its purpose. As a browser scripting language, JavaScript’s primary purpose is to interact with users and manipulate the DOM. This means that it has to be single-threaded, which can cause complex synchronization problems. For example, if there are two threads of JavaScript at the same time, one thread adds content to a DOM node, and the other thread removes that node, which thread should the browser use?

So, to avoid complexity, JavaScript has been single-threaded since its inception, and this has been a core feature of the language and will not change.

2. Js event loop

Js code execution process will have a lot of tasks, these tasks can be divided into two categories:

  • Synchronization task
  • Asynchronous tasks

When we open a website, the rendering process of the page is a lot of synchronization tasks, such as rendering the page skeleton and page elements. Tasks that take a lot of time, like loading pictures and music, are asynchronous tasks. , we use the map to illustrate:

Let’s explain this graph:

  • Synchronous and asynchronous tasks enter different execution “places”, synchronous to the main thread, asynchronous to the main threadEvent TableAnd register the function.
  • When the assigned task is complete,Event TableI’m going to move this function inEvent Queue.
  • Tasks in the main thread are empty and goEvent QueueRead the corresponding function, into the main thread execution.
  • The above process is repeated over and over again, as is often saidEvent Loop(Event loop).

So when is the main thread stack empty? The js engine has a monitoring process that continuously checks to see if the main thread stack is empty, and if it is, checks the Event Queue to see if there are any functions waiting to be called.

This is the whole process of JS running

It should be noted that in addition to synchronous tasks and asynchronous tasks, tasks can also be further subdivided into macrotasks and microtasks. Js engine will preferentially execute microtasks

Microtasks include promise callbacks, process.nexttick in Node, and mutationObservers that listen for Dom changes. Macro tasks include script execution,setTimeoutsetInterval, setImmediate mediate, and other timer events such as I/O, UI, etc.Copy the code

Recommended answers to an interview:

  1. First of all, JS runs in a single thread. When the code is executed, the execution context of different functions is pushed into the execution stack to ensure the orderly execution of the code.
  2. If an asynchronous event is encountered while executing synchronous code, the JS engine does not wait for the result to return. Instead, it suspends the event and continues executing other tasks in the execution stack
  3. When the synchronous event is complete, the callback corresponding to the asynchronous event is added to a different task queue from the current stack for execution.
  4. Task queue can be divided into macro task pair column and micro task pair column. When the execution of events in the current execution stack is completed, JS engine will first judge whether there are tasks in the micro task pair column that can be executed. If there are, the event at the top of the micro task queue will be pushed into the stack for execution.
  5. When the tasks in the microtask pair column are completed, the tasks in the macro task pair column are judged.
setTimeout(function() {
  console.log(1)},0);
new Promise(function(resolve, reject) {
  console.log(2);
  resolve()
}).then(function() {
  console.log(3)}); process.nextTick(function () {
  console.log(4)})console.log(5)


Copy the code

The first round: The main thread starts executing, hits setTimeout, throws the setTimeout callback function into the macro task queue, executes new Promise down, executes immediately, prints 2, then throws the callback function into the micro task queue, and continues executing, encounters process.nextTick. 5. When all synchronization tasks are completed, see if there are any microtasks that can be executed. Then function and nextTick microtasks are found. The asynchronous task specified by process.nextTick always precedes all asynchronous tasks, so process.nextTick output 4 is executed and then output 3 is executed, ending the first round of execution.

Round 2: Starting from the macro task queue, the setTimeout callback is found, and output 1 completes, so the result is 25431

31. Garbage collection mechanism

V8's garbage collection mechanism is based on generational collection, which in turn is based on the generation hypothesis, which has two characteristics: new objects tend to die early, and undead objects tend to live longer. Based on this hypothesis, v8 divides memory into the new generation and the old generation. Objects that are newly created or that have only undergone a garbage collection once are called the new generation. An object that has been garbage collected multiple times is called an old generation. The new generation is divided into "From" and "To", and "To" is generally idle. Recycle garbage by executing the Scavenge algorithm when the From space is full. The application logic will stop when we run the garbage collection algorithm and will continue when the garbage collection is finished. This algorithm is divided into three steps: (1) First check the surviving object of From space, if the object is alive, judge whether the object meets the condition of promotion to the old generation, if it meets the condition, it will be promoted to the old generation. If not, move To space. (2) If the object does not survive, the space of the object is freed. (3) Finally, the roles of From space and To space will be exchanged. The exploiture is based on two conditions. The first is to determine whether the object has been screcycled. If so, copy the object From the From space to the old generation; If not, it is copied To the To space. (2) The second is whether the memory usage ratio of To space exceeds the limit. When an object is copied From the From space To the To space, if more than 25% of the To space is used, the object is promoted directly To the old generation. The reason for setting 25% is that after the algorithm ends, the two Spaces will switch positions. If the memory of the To space is too small, the subsequent memory allocation will be affected. In the old generation, mark elimination method and mark compression method were used. Mark purge first marks the objects that are alive in memory, and then purges those that are not. Since tag clearing will cause a lot of memory fragmentation, it is not convenient for later memory allocation. Therefore, to solve the problem of memory fragmentation, mark compression method is introduced. Because the application logic will be suspended during garbage collection, for the new generation method, due to the small memory, each pause time is not too long, but for the old generation, each garbage collection time is long, the pause will cause a great impact. V8 addresses this problem by introducing incremental markers, which break a pause into multiple steps, each one of which allows the execution logic to run for a while, and so on.Copy the code

32. What operations cause memory leaks?

  • 1. Unexpected global variables

  • 2. Forgotten timers or callback functions

  • 3. Out-of-dom references

  • 4. The closure

  • The first is when we accidentally create a global variable by using an undeclared variable, leaving it in memory and unable to reclaim it.

  • The second case is that we set the setInterval timer and forget to cancel it. If the loop function has a reference to an external variable, that variable is left in memory and cannot be reclaimed.

  • The third case is when we get a reference to a DOM element that is deleted and cannot be reclaimed because we keep the reference to that element.

  • The fourth case is improper use of closures, resulting in some variables being left in memory.

33. What’s new in ECMAScript 2015 (ES6)?

  • Block scope
  • class
  • Arrow function
  • Template string
  • Reinforced object literals
  • Object to deconstruct
  • Promise
  • The module
  • Symbol
  • Proxy Set
  • Function default arguments
  • Rest and open

33.var.letandconstWhat is the difference between?

Variables declared by var are mounted on Windows, while variables declared by let and const are not:

var a = 100;
console.log(a,window.a);    / / 100 100

let b = 10;
console.log(b,window.b);    // 10 undefined

const c = 1;
console.log(c,window.c);    // 1 undefined

Copy the code

Let and const do not raise variables:

console.log(a); // undefined ===> a is declared and has not been assigned a value
var a = 100;

console.log(b); B is not defined ===
let b = 10;

console.log(c); C is not defined ===
const c = 10;

Copy the code

Let and const declarations form block scopes


if(1) {var a = 100;
  let b = 10;
}

console.log(a); / / 100
console.log(b)  B is not defined ===

-------------------------------------------------------------

if(1) {var a = 100;
  const c = 1;
}
console.log(a); / / 100
console.log(c)  C is not defined ===

Copy the code

Let and const cannot declare variables of the same name in the same scope, whereas var does

var a = 100;
console.log(a); / / 100

var a = 10;
console.log(a); / / 10
-------------------------------------
let a = 100;
let a = 10;

// console error: Identifier 'a' has already been declared ===>

Copy the code

Temporary dead zone

var a = 100;

if(1){
    a = 10;
    // If a is declared let/const in the current block scope, assigning a value of 10 to a will only look for variable A in the current scope.
    // Console Error: A is not defined
    let a = 1;
}

Copy the code

const


/* * 1, once the declaration must be assigned, do not use null placeholders. * * */; * * */; * * */

const a = 100; 

const list = [];
list[0] = 10;
console.log(list);/ / [10]

const obj = {a:100};
obj.name = 'apple';
obj.a = 10000;
console.log(obj);// {a:10000,name:'apple'}

Copy the code

34. What is an arrow function?

Arrow function expressions have a much cleaner syntax than function expressions and don’t have their own this, arguments, super, or new.target. The arrow function expression is more useful where anonymous functions are needed, and it cannot be used as a constructor.

//ES5 Version
var getCurrentDate = function (){
  return new Date(a); }//ES6 Version
const getCurrentDate = () = > new Date(a);Copy the code

In this case, the ES5 version has the function(){} declaration and the return keyword, which are needed to create the function and return value, respectively. In the arrow function version, we only need the () parentheses, not the return statement, because if we only have one expression or value to return, the arrow function will have an implicit return.

//ES5 Version
function greet(name) {
  return 'Hello ' + name + '! ';
}

//ES6 Version
const greet = (name) = > `Hello ${name}`;
const greet2 = name= > `Hello ${name}`;

Copy the code

We can also use the same arguments in arrow functions as function expressions and function declarations. If we have an argument in an arrow function, we can omit the parentheses.

const getArgs = () = > arguments

const getArgs2 = (. rest) = > rest

Copy the code

Arrow functions cannot access arguments objects. So calling the first getArgs function throws an error. Instead, we can use rest arguments to get all the arguments passed in the arrow function.

const data = {
  result: 0.nums: [1.2.3.4.5].computeResult() {
    // Where "this" refers to the "data" object
    const addAll = () = > {
      return this.nums.reduce((total, cur) = > total + cur, 0)};this.result = addAll(); }};Copy the code

Arrow functions do not have their own this value. It captures the this value of the lexical scope function. In this example, the addAll function copies the this value from computeResult, which is the Window object if we declare the arrow function in the global scope.

35. What is a class?

Classes are a new way of writing constructors in JS. It is syntactic sugar that uses constructors, but still stereotypes and prototype-based inheritance are used underneath.

 //ES5 Version
   function Person(firstName, lastName, age, address){
      this.firstName = firstName;
      this.lastName = lastName;
      this.age = age;
      this.address = address;
   }

   Person.self = function(){
     return this;
   }

   Person.prototype.toString = function(){
     return "[object Person]";
   }

   Person.prototype.getFullName = function (){
     return this.firstName + "" + this.lastName;
   }  

   //ES6 Version
   class Person {
        constructor(firstName, lastName, age, address){
            this.lastName = lastName;
            this.firstName = firstName;
            this.age = age;
            this.address = address;
        }

        static self() {
           return this;
        }

        toString(){
           return "[object Person]";
        }

        getFullName(){
           return `The ${this.firstName} The ${this.lastName}`; }}Copy the code

Override the method and inherit from another class.

//ES5 Version
Employee.prototype = Object.create(Person.prototype);

function Employee(firstName, lastName, age, address, jobTitle, yearStarted) {
  Person.call(this, firstName, lastName, age, address);
  this.jobTitle = jobTitle;
  this.yearStarted = yearStarted;
}

Employee.prototype.describe = function () {
  return `I am The ${this.getFullName()} and I have a position of The ${this.jobTitle} and I started at The ${this.yearStarted}`;
}

Employee.prototype.toString = function () {
  return "[object Employee]";
}

//ES6 Version
class Employee extends Person { //Inherits from "Person" class
  constructor(firstName, lastName, age, address, jobTitle, yearStarted) {
    super(firstName, lastName, age, address);
    this.jobTitle = jobTitle;
    this.yearStarted = yearStarted;
  }

  describe() {
    return `I am The ${this.getFullName()} and I have a position of The ${this.jobTitle} and I started at The ${this.yearStarted}`;
  }

  toString() { // Overriding the "toString" method of "Person"
    return "[object Employee]"; }}Copy the code

So how do we know it’s using a prototype internally?

class Something {}function AnotherSomething(){}const as = new AnotherSomething();
const s = new Something();

console.log(typeof Something); // "function"
console.log(typeof AnotherSomething); // "function"
console.log(as.toString()); // "[object Object]"
console.log(as.toString()); // "[object Object]"
console.log(as.toString === Object.prototype.toString); // true
console.log(s.toString === Object.prototype.toString); // true

Copy the code

36. What are template strings?

Template strings are a new way to create strings in JS. We can string templates by using backquotes.

//ES5 Version
var greet = 'Hi I\'m Mark';

//ES6 Version
let greet = `Hi I'm Mark`;

Copy the code

In ES5 we need to use some escape characters to achieve multi-line effects, but in template strings we don’t need to bother as much:

//ES5 Version
var lastWords = '\n'
  + ' I \n'
  + ' Am \n'
  + 'Iron Man \n';


//ES6 Version
let lastWords = ` I Am Iron Man `;

Copy the code

In the ES5 version, we need to add \n to add a new line to the string. In template strings, we don’t need to do this.

//ES5 Version
function greet(name) {
  return 'Hello ' + name + '! ';
}


//ES6 Version
function greet(name) {
  return `Hello ${name}! `;
}

Copy the code

In the ES5 version, if you need to add an expression or value to a string, you use the + operator. In the template string S, we can embed an expression with ${expr}, which makes it cleaner than the ES5 version.

37. What is object deconstruction?

Object destruction is a new, more concise way to get or extract values from objects or arrays. Suppose we have the following objects:

const employee = {
  firstName: "Marko".lastName: "Polo".position: "Software Developer".yearHired: 2017
};

Copy the code

To get a property from an object, the early method was to create a variable with the same name as the property of the object. This approach is cumbersome because we create a new variable for each attribute. Let’s say we have a large object that has a lot of properties and methods, and it’s cumbersome to extract the properties this way.

var firstName = employee.firstName;
var lastName = employee.lastName;
var position = employee.position;
var yearHired = employee.yearHired;

Copy the code

The syntax is much more concise with deconstruction:

let { firstName, lastName, position, yearHired } = employee;

Copy the code

We can also alias attributes:

let { firstName: fName, lastName: lName, position, yearHired } = employee;

Copy the code

Of course, if the property value is undefined, we can also specify the default value:

let { firstName = "Mark".lastName: lName, position, yearHired } = employee;

Copy the code

38. What isSetObject, how does it work?

The Set object allows you to store a unique value of any type, either a primitive value or an object reference.

We can use the Set constructor to create a Set instance.

const set1 = new Set(a);const set2 = new Set(["a"."b"."c"."d"."d"."e"]);
Copy the code

We can use the add method to add a new value to the Set instance. Since the add method returns a Set object, we can use Add again in chained fashion. If a value already exists in a Set object, it is no longer added.

set2.add("f");
set2.add("g").add("h").add("i").add("j").add("k").add("k");
// The latter "k" will not be added to the set object because it already exists

Copy the code

We can use the HAS method to check for the presence of a specific value in a Set instance.

set2.has("a") // true
set2.has("z") // true

Copy the code

We can use the size attribute to get the length of the Set instance.

set2.size // returns 10

Copy the code

You can use the clear method to delete data from a Set.

set2.clear();

Copy the code

We can use the Set object to remove duplicate elements from an array.

const numbers = [1.2.3.4.5.6.6.7.8.8.5];
const uniqueNums = [...new Set(numbers)]; / /,2,3,4,5,6,7,8 [1]

Copy the code

In addition, there is WeakSet, which is similar to Set and also a collection of non-repeating values. However, WeakSet members can only be objects, not other types of values. WeakSet objects are weak references, that is, garbage collection mechanism does not consider WeakSet’s reference to the object.

  • Map Data structure. It is a collection of key-value pairs similar to objects, but the range of “keys” is not limited to strings. Values of all types (including objects) can be used as keys.
  • WeakMap structure is similar to Map structure and is also used to generate a set of key-value pairs. However, WeakMap only accepts objects as key names (except null) and does not accept other types of values as key names. Moreover, the object to which the key name of WeakMap points is not included in the garbage collection mechanism.

39. What is Proxy?

Proxy is used to modify the default behavior of some operations, which is equivalent to making changes at the language level. Therefore, it is a kind of “metaprogramming”, that is, programming a programming language.

Proxy can be understood as a layer of “interception” before the target object. All external access to the object must pass this layer of interception. Therefore, Proxy provides a mechanism for filtering and rewriting external access. The word Proxy is used to mean that it acts as a Proxy for certain operations.

40. What is functional programming? What features of JavaScript make it a candidate for a functional language?

Functional programming (often abbreviated FP) is the process of building software by writing pure functions that avoid shared state, variable data, and side effects. Numerical programming is declarative rather than imperative, and application state flows through pure functions. In contrast to object-oriented programming, the state of an application is often shared and co-existed with methods in an object.

Functional programming is a programming paradigm, which means it’s a way of thinking about software construction based on some basic defining principles (listed above). Of course, other examples of programming paradigms include object-oriented programming and procedural programming.

Functional code tends to be cleaner, more predictable, and easier to test than imperative or object-oriented code – but without familiarity with it and the common patterns associated with it, functional code can also seem denser and cluttered, and the literature can be confusing to newcomers.

41. What is a higher-order function?

Higher-order functions are simply functions that take functions as arguments or return values.

function higherOrderFunction(param,callback){
    return callback(param);
}

Copy the code

42. Why are functions called first-class citizens?

In JavaScript, functions not only have everything that traditional functions do (declare and call), but they can behave like simple values:

  • Assignment (var func = function(){}),
  • The ginseng (function func(x,callback){callback(); }),
  • Return (function(){return function(){}}),

Such functions are also called first-class functions. In addition, functions in JavaScript act as constructors of classes and as instances of Function classes. Such multiple identities make JavaScript functions very important.

43. Deep copy? Shallow copy?

Deep and light copies of JavaScript have always been difficult, and if an interviewer asked me to write a deep copy now, I would probably only be able to write a basic version. So BEFORE I wrote this I read the blog posts of various big names in my favorites. For details, please refer to the link I posted below. Here is a brief summary.

  • Shallow copy: Creates a new object with an exact copy of the original object’s property values. If the property is a primitive type, it copies the value of the primitive type, and if the property is a reference type, it copies the memory address, so if one object changes the address, it affects the other object.
  • Deep copy: A complete copy of an object from the heap, creating a new area of the heap to store the new object, and modifying the new object does not affect the original object.

Shallow copy implementation:

  • Object.assign() method: Used to copy the values of all enumerable properties from one or more source objects to target objects. It will return the target object.
  • ** array.prototype.slice () : The **slice() method returns a new Array object that is a shallow copy of the original Array determined by begin and end (not including end). The original array will not be changed.
  • Extended operator.:
let a = {
    name: "Jake".flag: {
        title: "better day by day".time: "2020-05-31"}}letb = {... a};Copy the code

Deep copy implementation:

  • Parse (json.stringify (object)); Cannot resolve circular references; Cannot handle re, new Date())
  • Basic version: Shallow copy + recursion (only object and array data types are considered)
function cloneDeep(target,map = new WeakMap(a)) {
  if(typeOf taret ==='object') {let cloneTarget = Array.isArray(target) ? [] : {};
      
     if(map.get(target)) {
        return target;
    }
     map.set(target, cloneTarget);
     for(const key in target){
        cloneTarget[key] = cloneDeep(target[key], map);
     }
     return cloneTarget
  }else{
       return target
  }
 
}

Copy the code
  • The ultimate version:

    const mapTag = '[object Map]';
    const setTag = '[object Set]';
    const arrayTag = '[object Array]';
    const objectTag = '[object Object]';
    const argsTag = '[object Arguments]';
    
    const boolTag = '[object Boolean]';
    const dateTag = '[object Date]';
    const numberTag = '[object Number]';
    const stringTag = '[object String]';
    const symbolTag = '[object Symbol]';
    const errorTag = '[object Error]';
    const regexpTag = '[object RegExp]';
    const funcTag = '[object Function]';
    
    const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];
    
    
    function forEach(array, iteratee) {
        let index = -1;
        const length = array.length;
        while (++index < length) {
            iteratee(array[index], index);
        }
        return array;
    }
    
    function isObject(target) {
        const type = typeof target;
        returntarget ! = =null && (type === 'object' || type === 'function');
    }
    
    function getType(target) {
        return Object.prototype.toString.call(target);
    }
    
    function getInit(target) {
        const Ctor = target.constructor;
        return new Ctor();
    }
    
    function cloneSymbol(targe) {
        return Object(Symbol.prototype.valueOf.call(targe));
    }
    
    function cloneReg(targe) {
        const reFlags = /\w*$/;
        const result = new targe.constructor(targe.source, reFlags.exec(targe));
        result.lastIndex = targe.lastIndex;
        return result;
    }
    
    function cloneFunction(func) {
        const bodyReg = / (? <={)(.|\n)+(? =})/m;
        const paramReg = / (? < = \ () + (? =\)\s+{)/;
        const funcString = func.toString();
        if (func.prototype) {
            const param = paramReg.exec(funcString);
            const body = bodyReg.exec(funcString);
            if (body) {
                if (param) {
                    const paramArr = param[0].split(', ');
                    return new Function(... paramArr, body[0]);
                } else {
                    return new Function(body[0]); }}else {
                return null; }}else {
            return eval(funcString); }}function cloneOtherType(targe, type) {
        const Ctor = targe.constructor;
        switch (type) {
            case boolTag:
            case numberTag:
            case stringTag:
            case errorTag:
            case dateTag:
                return new Ctor(targe);
            case regexpTag:
                return cloneReg(targe);
            case symbolTag:
                return cloneSymbol(targe);
            case funcTag:
                return cloneFunction(targe);
            default:
                return null; }}function clone(target, map = new WeakMap(a)) {
    
        // Clone the original type
        if(! isObject(target)) {return target;
        }
    
        / / initialization
        const type = getType(target);
        let cloneTarget;
        if (deepTag.includes(type)) {
            cloneTarget = getInit(target, type);
        } else {
            return cloneOtherType(target, type);
        }
    
        // Prevent circular references
        if (map.get(target)) {
            return map.get(target);
        }
        map.set(target, cloneTarget);
    
        / / clone set
        if (type === setTag) {
            target.forEach(value= > {
                cloneTarget.add(clone(value, map));
            });
            return cloneTarget;
        }
    
        / / clone map
        if (type === mapTag) {
            target.forEach((value, key) = > {
                cloneTarget.set(key, clone(value, map));
            });
            return cloneTarget;
        }
    
        // Clone objects and arrays
        const keys = type === arrayTag ? undefined : Object.keys(target);
        forEach(keys || target, (value, key) = > {
            if (keys) {
                key = value;
            }
            cloneTarget[key] = clone(target[key], map);
        });
    
        return cloneTarget;
    }
    
    module.exports = {
        clone
    };
    
    Copy the code

44. What is a Promise? Can I make it by hand?

A Promise, which translates as a Promise, promises that it will give you a result over a period of time. Programmatically, Promise is a solution to asynchronous programming. Here’s a description of Promise in MDN:

A Promise object is a proxy object (proxy for a value), and the proxied value may not be known at the time the Promise object is created. It allows you to bind handlers for success and failure of asynchronous operations. This allows asynchronous methods to return values as synchronous methods do, but instead of immediately returning final execution results, a Promise object that represents future results.

A Promise has the following states:

  • Pending: The initial state, which is neither successful nor failed.
  • This is a pity: which means that the operation will be completed successfully.
  • Rejected: Indicates that the operation fails.

This promise can never be changed once it changes from the waiting state to the other state. That is to say, once the state becomes a pity/Rejected, it cannot be changed again. Promise may not make sense to you just because it’s a concept, but let’s take a simple chestnut;

Let’s say I have a girlfriend and it’s her birthday next Monday, and I promise to give her a surprise on her birthday, then from now on that promise goes into a waiting state, waiting for next Monday, and then the state changes. This is a big pity. If I give my girlfriend a surprise as promised next Monday, the state of the promise will change from pending to fulfilled successfully, which means that once the promise is fulfilled, there will be no other results, that is, the state will not change again. On the other hand, if I am too busy working overtime on the day and forget about it and the surprise I promised is not fulfilled, my status will change from pending to Rejected. Time cannot be turned back, so the status cannot change again.

The Promise object in the pending state will trigger the fulfilled/ Rejected state. Once the state changes, the then method of the Promise object will be called. Otherwise a catch is triggered. Let’s rewrite the code from the last callback to hell:

new Promise((Resolve to reject) = > {
     setTimeout(() = > {
            console.log(1)
            resolve()
        },1000)
        
}).then((res) = > {
    setTimeout(() = > {
            console.log(2)},2000)
}).then((res) = > {
    setTimeout(() = > {
            console.log(3)},3000)
}).catch((err) = > {
console.log(err)
})
Copy the code

There are some drawbacks to promises, such as the inability to cancel promises and the need to catch errors through callback functions.

Promise handwritten implementation, interview enough version:

function myPromise(constructor){
    let self=this;
    self.status="pending" // Define the initial state before the state changes
    self.value=undefined;// Determine the resolved state
    self.reason=undefined;// Define the state of the rejected state
    function resolve(value){
        // Two ==="pending" guarantees that state changes are irreversible
       if(self.status==="pending"){
          self.value=value;
          self.status="resolved"; }}function reject(reason){
        // Two ==="pending" guarantees that state changes are irreversible
       if(self.status==="pending"){
          self.reason=reason;
          self.status="rejected"; }}// Catch a construction exception
    try{
       constructor(resolve,reject);
    }catch(e){ reject(e); }}// Define the then method for chain calls
myPromise.prototype.then=function(onFullfilled,onRejected){
   let self=this;
   switch(self.status){
      case "resolved":
        onFullfilled(self.value);
        break;
      case "rejected":
        onRejected(self.reason);
        break;
      default:}}Copy the code

Promise.all()

Promise.race()

Similarities:

  • These are bothPromiseMethod, and the parameter passed is aPromiseThe array.
  • Will return onePromiseThe instance

The difference between:

  • “All” : all that is passed inPromiseIt all ends up being converted tofulfilledState, is executedresolveCallback and will return values yes allPromisetheresolveThe callbackvalueThe array. One of them any of themPromiseforrejectState, is returnedPromiseThe state of therejected.
  • Race: all incomingPromiseAny one of them is statically converted tofulfilledorrejected, the corresponding callback will be executed.

Usage scenarios

Use all when you want to combine multiple asynchronous results. When you want to control the timing of an asynchronous operation, you can use a timer and a race.

Passing an empty array

What happens when our all and race pass in an empty array?

  • All: the returnedPromiseWill enterfullfilledState, then it will be executedresolveTo return an empty array
  • Race: the returnedPromiseIt’s going to stay therependingState.

45.GeneratorWhat is the function? What does it do?

The Generator functions are a concrete implementation of the Iterator interface. The most important feature of the Generator is that it can control the execution of functions.

function *foo(x) {
  let y = 2 * (yield (x + 1))
  let z = yield (y / 3)
  return (x + y + z)
}
let it = foo(5)
console.log(it.next())   // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8, done: false}
console.log(it.next(13)) // => {value: 42, done: true}

Copy the code

The above example is a Generator function. Let’s examine its execution:

  • First when the Generator function is called it returns an iterator
  • When next is executed for the first time, the pass is ignored and the function pauses at yield (x + 1), so 5 + 1 = 6 is returned
  • When next is executed the second time, the argument passed is equal to the return value of the previous yield, which always returns undefined if you pass no arguments. Let y = 2 * 12, so the second yield is equal to 2 * 12/3 = 8
  • When next is executed the third time, the passed arguments are passed to Z, so z = 13, x = 5, and y = 24 add up to 42

Generator functions are not commonly seen, and are generally used with co libraries. Of course, we can solve the problem of callback hell with Generator functions.

46. What isasync/awaitHow does it work? What are the pros and cons?

Async /await is a new way of writing asynchronous or non-blocking code based on promises and is generally considered the ultimate and most elegant solution to ASYNCHRONOUS JS operations. It is more readable and concise than promises and callbacks. After all, then() is also annoying.

‘async’ means’ asynchronous’ and ‘await’ is short for ‘async wait’.

So it is semantically understandable that async is used to declare that a function is asynchronous and await is used to wait for an asynchronous method to complete.

If async is added to a function, the function returns a Promise

Async function test() {return "1"} console.log(test()) // -> Promise {<resolved>: "1"} copy codeCopy the code

You can see that the output is a Promise object. Therefore, async returns a Promise object. If an async function returns a Promise object directly, async returns the Promise object wrapped in promise.resolve ().

Async /await handles then chains better than Promise

function takeLongTime(n) {
    return new Promise(resolve= > {
        setTimeout(() = > resolve(n + 200), n);
    });
}

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}

function step3(n) {
    console.log(`step3 with ${n}`);
    returntakeLongTime(n); } Duplicate codeCopy the code

We now implement these three steps with Promise and async/await, respectively.

The use of Promise

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2= > step2(time2))
        .then(time3= > step3(time3))
        .then(result= > {
            console.log(`result is ${result}`);
        });
}
doIt();
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
Copy the code

useasync/await

async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
}
doIt();
Copy the code

The result is the same as the previous Promise implementation, but the code looks much cleaner, more elegant, and almost identical to synchronous code.

The await keyword can only be used in async functions. Using the await keyword in any function that is not async function throws an error. The await keyword waits for the expression on the right (perhaps a Promise) to return before executing the next line of code. Copy the codeCopy the code

The advantages and disadvantages:

Async /await has the advantage of handling then call chains, writing code more clearly and accurately, and also gracefully solving callback hell. Of course, there are some disadvantages, because await transforms asynchronous code into synchronous code, and if multiple asynchronous code uses await without dependencies, it will result in performance degradation.

47. Js throttling and anti-shake

Function stabilization means that the callback is executed after the event is triggered for n seconds. If the event is triggered again within n seconds, the timer is reset. This can be used on some click-request events to avoid sending multiple requests to the back end because the user clicks multiple times.

Function throttling refers to the specified unit of time. Within this unit of time, only one callback function that triggers an event can be executed. If an event is triggered multiple times within the same unit of time, only one callback function can take effect. Throttling can be used to reduce the frequency of event calls through event throttling on scroll function event listeners.


// Function stabilization implementation
function debounce(fn, wait) {
  var timer = null;

  return function() {
    var context = this,
      args = arguments;

    // If there is a timer, cancel the previous timer and reset the time
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }

    // Set the timer so that the event interval is executed after the specified event
    timer = setTimeout(() = > {
      fn.apply(context, args);
    }, wait);
  };
}

// Function throttling implementation;
function throttle(fn, delay) {
  var preTime = Date.now();

  return function() {
    var context = this,
      args = arguments,
      nowTime = Date.now();

    // If the interval exceeds the specified time, the function is executed.
    if (nowTime - preTime >= delay) {
      preTime = Date.now();
      returnfn.apply(context, args); }}; }Copy the code

48. What are design patterns?

1. The concept

A design pattern is a set of repeated, well-known, catalogued code design lessons. Design patterns are used to reuse code, make it easier for others to understand, and ensure code reliability. There is no doubt that design patterns are a win-win for yourself, others and systems. Design patterns make coding truly engineering. Design patterns are the cornerstones of software engineering, like the bricks and stones of a mansion.

2. Design principles
  1. S — Single Responsibility Principle
    • A program only does one thing
    • If the functionality is too complex, break it down and keep each part separate
  2. O — OpenClosed Principle
    • Open to extension, closed to modification
    • When adding requirements, extend new code, not modify existing code
  3. L — Liskov Substitution Principle
    • A subclass can override its parent class
    • Subclasses can appear wherever a parent class can
  4. I-interface Segregation Principle Interface Segregation Principle
    • Keep interfaces single and separate
    • Similar to the single responsibility principle, there is a greater focus on interfaces
  5. D — Dependency Inversion Principle
    • Interface – oriented programming relies on abstractions rather than tools
    • The user only cares about the interface and not the implementation of the concrete class
3. Types of design patterns
  1. Structural Patterns: Simplify system design by identifying simple relationships between components in a system.
  2. Creational Patterns: Handles the creation of objects, creating them in a way that suits the situation. Regular object creation can cause design problems or add complexity to your design. The creative pattern solves the problem by controlling the creation of objects in some way.
  3. Behavioral Patterns: Used to identify common interaction Patterns between objects and implement them, thus increasing the flexibility of those interactions.

49.9 Common front-end design patterns

1. Facade Pattern

The facade pattern is one of the most common design patterns that provides a unified high-level interface for a set of interfaces in a subsystem, making it easier to use. In a nutshell, the design pattern abstracts complex logic from multiple subsystems to provide a more uniform, concise, and easy-to-use API. Many commonly used frameworks and libraries follow the same design pattern. JQuery, for example, abstracts and encapsulates complex native DOM manipulation, eliminating interoperability issues between browsers, and providing a more advanced and easy-to-use version. We often use appearance patterns in our work, but we don’t know it.

  1. Compatible with browser event binding
let addMyEvent = function (el, ev, fn) {
    if (el.addEventListener) {
        el.addEventListener(ev, fn, false)}else if (el.attachEvent) {
        el.attachEvent('on' + ev, fn)
    } else {
        el['on' + ev] = fn
    }
}; 
Copy the code
  1. Encapsulated interface
let myEvent = {
    // ...
    stop: e= >{ e.stopPropagation(); e.preventDefault(); }}; Copy the codeCopy the code
scenario
  • Early in the design process, there should be a conscious separation of the two different layers, such as the classic three-tier structure, with a Facade between the data access layer and the business logic layer, and between the business logic layer and the presentation layer
  • During development, subsystems tend to become more complex as they evolve through refactoring, and adding a Facade can provide a simple interface that reduces dependencies between them.
  • Using a Facade is also appropriate when maintaining a large legacy system that may already be difficult to maintain. Develop a Facade class for the system to provide a clear interface to poorly designed and highly complex legacy code to allow the new system to interact with Facade objects. The Facade interacts with the legacy code for all the complex work.
advantages
  • Reduce system interdependence.
  • Increase flexibility.
  • Improved security
disadvantages
  • Inconsistent open and close principle, if you want to change things is very troublesome, inheritance rewrite are not appropriate.
2. Proxy Pattern

Is to provide a proxy or placeholder for an object to control access to it

Assuming that A receives flowers when she is in A good mood, xiaoming has A chance of success

60%, and when A received flowers when she was in A bad mood, the success rate of Xiao Ming’s confession approached 0. Xiao Ming and A have only known each other for two days and still can’t tell when A is in A good mood. If you send the flowers to A in an inappropriate way, there is A high possibility that the flowers will be thrown away directly. Xiao Ming got the flowers after eating instant noodles for seven days. However, A’s friend B knows A very well, so Xiaoming just gives the flower to B, and B will monitor A’s mood change and then choose A to give the flower to A when A is in A good mood, the code is as follows:

let Flower = function() {}
let xiaoming = {
  sendFlower: function(target) {
    let flower = new Flower()
    target.receiveFlower(flower)
  }
}
let B = {
  receiveFlower: function(flower) {
    A.listenGoodMood(function() {
      A.receiveFlower(flower)
    })
  }
}
let A = {
  receiveFlower: function(flower) {
    console.log('Received flowers'+ flower)
  },
  listenGoodMood: function(fn) {
    setTimeout(function() {
      fn()
    }, 1000)
  }
}
xiaoming.sendFlower(B)
Copy the code
scenario
  • HTML element event broker
<ul id="ul">
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>
<script>
  let ul = document.querySelector('#ul');
  ul.addEventListener('click'.event= > {
    console.log(event.target);
  });
</script>
Copy the code
  • ES6 proxy Ruan Yifeng Proxy
  • JQuery. The proxy () method
advantages
  • The proxy mode can separate the proxy object from the called object and reduce the coupling degree of the system. The proxy mode acts as an intermediary between the client and the target object, thus protecting the target object
  • Proxy objects extend the functionality of target objects; By modifying the proxy object is ok, in line with the open and close principle;
disadvantages
  • The speed of processing requests can vary, and indirect access has overhead
3. Factory Pattern

The factory pattern defines an interface for creating objects that subclasses decide which class to instantiate. This pattern delays instantiation of a class to subclasses. Subclasses can override interface methods to specify their own object types when they are created.

class Product {
    constructor(name) {
        this.name = name
    }
    init() {
        console.log('init')}fun() {
        console.log('fun')}}class Factory {
    create(name) {
        return new Product(name)
    }
}

// use
let factory = new Factory()
let p = factory.create('p1') p.it () p.sun () copy the codeCopy the code
scenario
  • The factory pattern is ideal if you don’t want strong coupling between one subsystem and the larger object, but want the runtime to pick and choose from many subsystems
  • Encapsulate the new operation simply. When encountering new, we should consider whether to use factory mode.
  • When you need to create different instances that have the same behavior depending on the specific environment, you can use the factory pattern to simplify the implementation process and reduce the amount of code required for each object, which helps eliminate the coupling between objects and provides greater flexibility

advantages

  • The process of creating an object can be complex, but we only need to care about the result.
  • Constructor and creator separated, in accordance with the “open closed principle”
  • A caller who wants to create an object needs only to know its name.
  • High scalability. If you want to add a product, just extend a factory class.
disadvantages
  • When new products are added, new specific product classes need to be written, which increases the complexity of the system to a certain extent
  • Considering the scalability of the system, it is necessary to introduce an abstraction layer, which is used to define the client code, increasing the abstraction and difficulty of understanding the system
When not to use it
  • When applied to the wrong type of problem, this pattern introduces a great deal of unnecessary complexity into the application. Unless providing an interface for creating objects is a design goal of the library or framework we write, I recommend using explicit constructors to avoid unnecessary overhead.
  • Because of the fact that the object creation process is efficiently abstracted behind an interface, this also poses problems for unit tests that depend on how complex the process may be.
4. Singleton Pattern

As the name implies, the number of instances of Class in a singleton pattern is at most 1. The singleton pattern comes in handy when you need an object to perform some task across the system. Other scenarios try to avoid singleton patterns because singleton patterns introduce global state, and a healthy system should avoid introducing too much global state.

The following problems need to be solved to implement the singleton pattern:

  • How do I determine that Class has only one instance?
  • How can I easily access a unique instance of Class?
  • How does Class control the instantiation process?
  • How do I limit the number of instances of Class to 1?

We generally solve these problems by implementing the following two things:

  • Hide Class constructors to avoid multiple instantiations
  • Create/get a unique instance by exposing a getInstance() method

The singleton pattern in Javascript can be implemented in the following ways:

// singleton constructor
const FooServiceSingleton = (function () {
  // The Class constructor is hidden
  function FooService() {}

  // Uninitialized singleton
  let fooService;

  return {
    // A function to create/get a singleton
    getInstance: function () {
      if(! fooService) { fooService =new FooService();
      }
      returnfooService; }}}) ();Copy the code

Key points of implementation are:

  1. Create local scopes with IIFE and execute them immediately;
  2. getInstance() Is a closure that is used to hold and return a singleton object in a local scope.

We can verify that the singleton object was created successfully:

const fooService1 = FooServiceSingleton.getInstance();
const fooService2 = FooServiceSingleton.getInstance();

console.log(fooService1 === fooService2); // true
Copy the code
Example scenario
  • Define namespaces and implement branching methods
  • The login dialog
  • Store in vuex and Redux
advantages
  • Partition the namespace and reduce global variables
  • Improve modularity, organize your code in a global variable name, in a single location, easy maintenance
  • And it will only be instantiated once. Simplifies code debugging and maintenance
disadvantages
  • Because the singleton pattern provides a single point of access, it can lead to strong coupling between modules
  • This is bad for unit testing. You can’t test a class that calls a method from a singleton by itself, you can only test it with that singleton
  • Test each unit together.
5. Strategy Pattern

The strategy pattern is simply described as: the object has a certain behavior, but in different scenarios, the behavior has different implementation algorithms. Encapsulate them one by one and make them interchangeable

<html>
<head>
    <title>Policy mode - Validation form</title>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body>
    <form id = "registerForm" method="post" action="http://xxxx.com/api/register">User name:<input type="text" name="userName">Password:<input type="text" name="password">Mobile Phone Number:<input type="text" name="phoneNumber">
        <button type="submit">submit</button>
    </form>
    <script type="text/javascript">
        // Policy object
        const strategies = {
          isNoEmpty: function (value, errorMsg) {
            if (value === ' ') {
              returnerrorMsg; }},isNoSpace: function (value, errorMsg) {
            if (value.trim() === ' ') {
              returnerrorMsg; }},minLength: function (value, length, errorMsg) {
            if (value.trim().length < length) {
              returnerrorMsg; }},maxLength: function (value, length, errorMsg) {
            if (value.length > length) {
              returnerrorMsg; }},isMobile: function (value, errorMsg) {
            if (!/^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[7]|18[0|1|2|3|5|6|7|8|9])\d{8}$/.test(value)) {
              returnerrorMsg; }}}/ / verification
        class Validator {
          constructor() {
            this.cache = []
          }
          add(dom, rules) {
            for(let i = 0, rule; rule = rules[i++];) {
              let strategyAry = rule.strategy.split(':')
              let errorMsg = rule.errorMsg
              this.cache.push(() = > {
                let strategy = strategyAry.shift()
                strategyAry.unshift(dom.value)
                strategyAry.push(errorMsg)
                return strategies[strategy].apply(dom, strategyAry)
              })
            }
          }
          start() {
            for(let i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
              let errorMsg = validatorFunc()
              if (errorMsg) {
                return errorMsg
              }
            }
          }
        }

        // Call code
        let registerForm = document.getElementById('registerForm')

        let validataFunc = function() {
          let validator = new Validator()
          validator.add(registerForm.userName, [{
            strategy: 'isNoEmpty'.errorMsg: 'User name cannot be empty'
          }, {
            strategy: 'isNoSpace'.errorMsg: 'Whitespace character names are not allowed'
          }, {
            strategy: 'minLength:2'.errorMsg: 'Username must be at least 2 characters long'
          }])
          validator.add(registerForm.password, [ {
            strategy: 'minLength:6'.errorMsg: 'Password length must not be less than 6 characters'
          }])
          validator.add(registerForm.phoneNumber, [{
            strategy: 'isMobile'.errorMsg: 'Please enter the correct mobile phone number format'
          }])
          return validator.start()
        }

        registerForm.onsubmit = function() {
          let errorMsg = validataFunc()
          if (errorMsg) {
            alert(errorMsg)
            return false}}</script>
</body>
</html>

Copy the code
Example scenario
  • If there are many classes in a system that are distinguished only by their ‘behavior’, then using the policy pattern can dynamically let an object choose one behavior among many behaviors.
  • A system needs to dynamically choose one of several algorithms.
  • Form validation
advantages
  • Multiple conditional selection statements can be effectively avoided by using combination, delegate and polymorphism techniques
  • Provides perfect support for the open-closed principle, encapsulating algorithms in a separate strategy, making them easy to switch, understand, and extend
  • Using composition and delegation to give the Context the ability to perform the algorithm is also a lighter alternative to inheritance
disadvantages
  • Many policy classes or policy objects are added to the program
  • To use the strategy pattern, it is necessary to understand all strategies, and to understand the differences between strategies in order to choose a suitable strategy
6. Iterator Pattern

If you see this, the Iterator in ES6 is probably familiar to you, as outlined in item 60 above. The iterator pattern simply provides a way to order the elements of an aggregate object without exposing the internal representation of that object.

The iterator pattern solves the following problems:

  • Provides a consistent way to traverse various data structures without understanding the internal structure of the data
  • Provides the ability to traverse a container (collection) without changing the container’s interface

An iterator typically needs to implement the following interfaces:

  • HasNext () : Checks if the iteration is over, returns Boolean
  • Next () : Finds and returns the next element

To implement an iterator for a Javascript array, write:

const item = [1.'red'.false.3.14];

function Iterator(items) {
  this.items = items;
  this.index = 0;
}

Iterator.prototype = {
  hasNext: function () {
    return this.index < this.items.length;
  },
  next: function () {
    return this.items[this.index++]; }}Copy the code

Verify that the iterator works:

const iterator = new Iterator(item);

while(iterator.hasNext()){
  console.log(iterator.next());
}
// Output: 1, red, false, 3.14
Copy the code

ES6 provides a simpler iterative loop syntax for… Iterator (Symbol. Iterator) : iterator (Symbol. Iterator) : iterator (Symbol.

For example, we implement a Range class to iterate over a Range of numbers:

function Range(start, end) {
  return{[Symbol.iterator]: function () {
      return {
        next() {
          if (start < end) {
            return { value: start++, done: false };
          }
          return { done: true.value: end };
        }
      }
    }
  }
}

Copy the code

Verify:

for (num of Range(1.5)) {
  console.log(num);
}
// Output: 1, 2, 3, 4
Copy the code
7. Observer Pattern

The Observer Pattern, also known as the Publish/Subscribe Pattern, is a design Pattern we often encounter. It is also widely used in daily life. For example, if you Subscribe to a blogger’s channel, you will receive notifications when there are updates. Another example is the event subscription response mechanism in JavaScript. The idea of observer mode is described in one sentence: the subject maintains a group of observers. When the state of the observed object changes, it notifies the observer of these changes by calling a method of the observer.

The Subject object in observer mode generally needs to implement the following apis:

  • Subscribe (): Receives an observer observer and causes it to subscribe itself
  • Unsubscribe (): Receives an observer observer and causes it to unsubscribe itself
  • Fire (): Triggers an event that notifies all observers

Implement observer mode manually with JavaScript:

// Observed
function Subject() {
  this.observers = [];
}

Subject.prototype = {
  / / subscribe
  subscribe: function (observer) {
    this.observers.push(observer);
  },
  // Unsubscribe
  unsubscribe: function (observerToRemove) {
    this.observers = this.observers.filter(observer= > {
      return observer !== observerToRemove;
    })
  },
  // The event is triggered
  fire: function () {
    this.observers.forEach(observer= >{ observer.call(); }); }}Copy the code

Verify that the subscription was successful:

const subject = new Subject();

function observer1() {
  console.log('Observer 1 Firing! ');
}


function observer2() {
  console.log('Observer 2 Firing! ');
}

subject.subscribe(observer1);
subject.subscribe(observer2);
subject.fire();

/ / output:
Observer 1 Firing! 
Observer 2 Firing!
Copy the code

Verify that the unsubscription was successful:

subject.unsubscribe(observer2);
subject.fire();

/ / output:
Observer 1 Firing!
Copy the code
scenario
  • DOM events
document.body.addEventListener('click'.function() {
    console.log('hello world! ');
});
document.body.click()
Copy the code
  • Vue responsive

advantages

  • Supports simple broadcast communication that automatically notifies all objects that have subscribed
  • The abstract coupling between the target object and the observer can be independently extended and reused
  • Increased flexibility
  • What the observer pattern does is decouple, making both sides of the coupling dependent on the abstract rather than the concrete. So that the changes in each side don’t affect the changes in the other side.
disadvantages
  • Overuse can weaken the relationship between objects and make programs difficult to track, maintain and understand
8. Mediator Pattern

In the Mediator pattern, mediators wrap the way in which a series of objects interact, so that the objects do not have to interact directly, but rather the Mediator coordinates their interactions so that they can be loosely coupled. When the functions of some objects change, the functions of other objects are not affected immediately. These functions can be changed independently of each other.

The mediator mode is similar to the Observer mode in that both are one-to-many and centralized communication. The difference is that the mediator mode deals with the interaction between peer objects while the Observer mode deals with the interaction between the Observer and the Subject. The intermediary model is a bit like the matchmaking agency. Blind dates cannot communicate directly at the beginning, but need to select and match through the intermediary before deciding who to meet with whom.

scenario
  • For example, if there is a shopping cart demand, there is an item selection form, a color selection form, a purchase quantity form and so on, all of these events will trigger the change event. Then, these events can be forwarded and processed through the intermediary to realize the decoupling between events, and only the intermediary object can be maintained.
var goods = {   // Mobile phone inventory
    'red|32G': 3.'red|64G': 1.'blue|32G': 7.'blue|32G': 6};/ / broker
var mediator = (function() {
    var colorSelect = document.getElementById('colorSelect');
    var memorySelect = document.getElementById('memorySelect');
    var numSelect = document.getElementById('numSelect');
    return {
        changed: function(obj) {
            switch(obj){
                case colorSelect:
                    //TODO
                    break;
                case memorySelect:
                    //TODO
                    break;
                case numSelect:
                    //TODO
                    break; }}}}) (); colorSelect.onchange =function() {
    mediator.changed(this);
};
memorySelect.onchange = function() {
    mediator.changed(this);
};
numSelect.onchange = function() {
    mediator.changed(this);
};

Copy the code
  • Chat room

Chatroom member class:

function Member(name) {
  this.name = name;
  this.chatroom = null;
}

Member.prototype = {
  // Send a message
  send: function (message, toMember) {
    this.chatroom.send(message, this, toMember);
  },
  // Receive the message
  receive: function (message, fromMember) {
    console.log(`${fromMember.name} to The ${this.name}: ${message}`); }}Copy the code

Chat room class:

function Chatroom() {
  this.members = {};
}

Chatroom.prototype = {
  // Add members
  addMember: function (member) {
    this.members[member.name] = member;
    member.chatroom = this;
  },
  // Send a message
  send: function (message, fromMember, toMember) { toMember.receive(message, fromMember); }}Copy the code

Test it out:

const chatroom = new Chatroom();
const bruce = new Member('bruce');
const frank = new Member('frank');

chatroom.addMember(bruce);
chatroom.addMember(frank);

bruce.send('Hey frank', frank);

Bruce to Frank: Hello Frank
Copy the code
advantages
  • Loose coupling between objects and the ability to change their interactions independently
  • The one-to-many relationship between intermediaries and objects replaces the net-like many-to-many relationship between objects
  • If the complexity of the coupling between objects makes maintenance difficult, and the coupling increases rapidly from project to project, an intermediary needs to refactor the code
disadvantages
  • A new mediator object is added to the system, because the complexity of the interaction between objects is transferred to the complexity of the mediator object, making the mediator object often huge. The mediator object itself is often a difficult object to maintain.
9. Visitor Pattern

The visitor pattern is a design pattern that separates algorithms from object structures. In plain English, the visitor pattern allows us to add new logic to an object without changing its structure. The new logic is stored in a separate visitor object. The visitor pattern is often used to extend third-party libraries and tools.

/ / visitors
class Visitor {
    constructor() {}
    visitConcreteElement(ConcreteElement) {
        ConcreteElement.operation()
    }
}
/ / element class
class ConcreteElement{
    constructor(){}operation() {
       console.log("ConcreteElement.operation invoked");  
    }
    accept(visitor) {
        visitor.visitConcreteElement(this)}}// client
let visitor = new Visitor()
let element = new ConcreteElement()
elementA.accept(visitor)
Copy the code

The visitor pattern is implemented with the following elements:

  • Visitor Object: Visitor Object that has onevisit()methods
  • Receiving Object: Receiving objects accept()methods
  • Visit (receivingObj) : Used for Visitor to receive oneReceiving Object
  • Accept (the visitor) : used toReceving ObjectReceive a Visitor and pass the callVisitorthevisit()Provide fetch for itReceiving ObjectThe power of data

The simple code implementation is as follows:

Receiving Object:function Employee(name, salary) {
  this.name = name;
  this.salary = salary;
}

Employee.prototype = {
  getSalary: function () {
    return this.salary;
  },
  setSalary: function (salary) {
    this.salary = salary;
  },
  accept: function (visitor) {
    visitor.visit(this);
  }
}
Visitor Object:function Visitor() { }

Visitor.prototype = {
  visit: function (employee) {
    employee.setSalary(employee.getSalary() * 2); }}Copy the code

Verify:

const employee = new Employee('bruce'.1000);
const visitor = new Visitor();
employee.accept(visitor);

console.log(employee.getSalary());// Output: 2000
Copy the code
scenario
  • The class corresponding to an object in an object structure rarely changes, but it is often necessary to define new operations on this object structure
  • You need to do many different and unrelated operations on objects in an object structure, and you want to avoid having those operations “contaminate” the classes of those objects, and you don’t want to modify those classes when you add new operations.
advantages
  • Consistent with the principle of single responsibility
  • Excellent scalability
  • flexibility
disadvantages
  • Specific elements disclose details to visitors, violating the Demeter principle
  • Violates the dependency inversion principle by relying on concrete classes instead of abstractions.
  • Specific element changes are difficult