preface

I just want to meet a CV engineer, the interviewer let me challenge build rocket engineer, plus this year this situation is before and after two men, but again difficult to live, or continue to find a job. In the recent interview, I have been summarizing, and every time I come back from the interview, THE following are the interview knowledge points I met in the past few days. However, today’s topic is 66 JavaScript knowledge points written in the title, from the shallow to the deep, organized a week, every (zhěng) day (lǐ) whole (bu) (yi) 10 (qiu) left (diǎn) right (zan), hope to find a job a little help, the text if there is wrong, please also point out.

HTML&CSS:

  • Browser kernel
  • Box model, Flex layout, two/three column layout, horizontal/vertical center;
  • BFC, clear float;
  • Css3 animation, H5 new features.

JavaScript:

  • Inheritance, prototype chain, this point, design pattern, call, apply, bind,
  • New implementation, shake throttling, let, var, const difference, temporary dead zone, event, loop;
  • Promise use and implementation, promise parallel execution and sequential execution;
  • Async /await advantages and disadvantages;
  • Closures, garbage collection and memory leaks, array methods, array out-of-order, array flattening, event delegates, event listeners, event models

Vue:

  • Vue data bidirectional binding principle;
  • Vue computed and the difference between computed and watch.
  • Vue compiler structure diagram, life cycle, vUE component communication;
  • MVVM pattern, MVC pattern understanding;
  • Vue Dom Diff, vuex, and Vue-Router

Network:

  • HTTP1, HTTP2, HTTPS, common HTTP status code;
  • Browse what happens from typing a url to hitting enter;
  • Front-end security (CSRF, XSS)
  • Front-end cross-domain, browser cache, cookie, Session, Token, LocalStorage, sessionStorage;
  • TCP connection (three handshakes, four waves)

Performance related

  • Image optimization
  • 500 images, how to achieve preload optimization
  • Lazy load implementation
  • Ways to reduce HTTP requests
  • How does WebPack configure large projects

In addition, I am also in the process of sorting out the more comprehensive test set. Here is a preview:

Let’s get to the point:

What are the data types of JS and how are the values stored

See my previous article: “Front end package” is probably the most thorough explanation of JavaScript data types

There are eight data types in JavaScript. There are seven basic data types: Undefined, Null, Boolean, Number, String, Symbol (added in ES6 to represent unique values) and BigInt (added in ES10).

1 reference data type — Object (An Object is essentially an unordered set of name-value pairs). Contains function, Array, Date, and so on. JavaScript does not support any mechanism for creating custom types, and all values will ultimately be one of the eight data types described above.

Raw data type: Directly stored in the stack (stack), occupies small space, size is fixed, is frequently used data, so put into the stack storage.

Reference data type: Stored in both stack and heap, occupying large space and variable size. The reference data type stores a pointer on the stack to the starting address of the entity in the heap. When the interpreter looks for a reference value, it first retrieves its address in the stack and then retrieves the entity from the heap.

2, &&, | | and!!!!! What do operators do separately

  • &&Called the logical and, finds the first virtual expression among its operands and returns it, or returns the last true expression if no virtual expression is found. It uses short circuit to prevent unnecessary work.
  • ||Called logical or, finds the first truth expression in its operands and returns it. This also uses a short circuit to prevent unnecessary work. It is used to initialize default parameter values in a function before ES6 default function parameters are supported.
  • !!!!!Operator to cast the value on the right to a Boolean value, which is an easy way to convert a value to a Boolean value.

3. Js data type conversion

There are only three types of cast in JS, and they are:

  • Convert to a Boolean value (call the Boolean() method)
  • Convert to a Number(call the Number(), parseInt(), and parseFloat() methods)
  • To a String(call.tostring () or String())
Null and underpaid have no. ToString methodCopy the code

In addition, some operators will have implicit conversion, not to expand here, can be baidu 00

4. The data type of JS (typeof, instanceof, constructor, the Object. The prototype. ToString, call ()

(1) the typeof

For primitive types, the correct type can be displayed except for null

console.log(typeof 2); // number console.log(typeof true); // boolean console.log(typeof 'str'); // string console.log(typeof []); // object [] the data typeof the array is interpreted in typeof as object console.log(typeof function(){}); // function console.log(typeof {}); // object console.log(typeof undefined); // undefined console.log(typeof null); // the data typeof object null is interpreted by typeof as objectCopy the code

For objects, typeof displays objects except functions. Therefore, typeof cannot accurately determine the typeof a variable. Therefore, if you want to determine the correct typeof an object, you can consider using instanceof

(2) instanceof

Instanceof can correctly determine the type of the object, because the internal mechanism is to determine whether the prototype of the type can be found in the prototype chain of the object.

console.log(2 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false  
console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log({} instanceof Object);                   // true    
// console.log(undefined instanceof Undefined);
// console.log(null instanceof Null);
Copy the code

It can be seen that the direct literal value determines the data type. Instanceof can accurately determine the reference data type (Array, Function, Object), while the basic data type cannot be accurately determined by Instanceof.

The instanceof operator tests whether an object has a prototype property in its prototype chain. This means determining whether an object is an instance of a data type (such as Array). Focus on determining whether an object is an instance of a data type. In this case, the literals, 2, true, and ‘STR’ are not instances, so the judgment is false.

(3) the constructor

console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true
Copy the code
There is a pitfall here that if I create an object and change its prototype, Constructor will become unreliableCopy the code
function Fn(){};
 
Fn.prototype=new Array();
 
var f=new Fn();
 
console.log(f.constructor===Fn);    // false
console.log(f.constructor===Array); // true 
Copy the code

(4) the Object. The prototype. ToString. The call Object prototype method toString (), using the call for civet cats in prince, to borrow from the Object’s toString method

var a = Object.prototype.toString;
 
console.log(a.call(2));
console.log(a.call(true));
console.log(a.call('str'));
console.log(a.call([]));
console.log(a.call(function(){}));
console.log(a.call({}));
console.log(a.call(undefined));
console.log(a.call(null));
Copy the code

What are the built-in objects in JS?

Built-in objects in JS mainly refer to some global value properties, functions and constructor functions defined by JS that exist in global scope before program execution. We often use global variables such as NaN and undefined, global functions such as parseInt() and parseFloat() to instantiate Object constructors such as Date and Object, and individual built-in objects that provide mathematical computations such as the Math Object.

Related knowledge:

Global Objects, or standard built-in objects, are not to be confused with global Objects. By global objects, WE mean objects in the global scope. Other objects in the global scope can be created by a user's script or provided by a host program. The class (1) value properties of standard built-in objects. These global properties return a simple value that has no properties or methods of its own. For example, the Infinity, NaN, undefined, and NULL literals (2) are function attributes. Global functions can be called directly without specifying the owning object. After execution, the result will be returned to the caller directly. Examples include eval(), parseFloat(), parseInt(), etc. (3) Basic objects, which are the basis for defining or using other objects. Basic objects include generic objects, function objects, and error objects. For example, Object, Function, Boolean, Symbol, Error, etc. (4) Numeric and date objects, used to represent numbers, dates, and objects that perform mathematical calculations. For example, the Number, Math, and Date (5) strings are used to represent and manipulate the object of the string. Examples are String, RegExp (6) indexable collection objects that represent collections of data sorted by index values, including arrays and arrays of types, as well as array-like objects. For example, Array (7) is a collection object that uses keys to store data and supports iterating elements in insertion order. For example, Map, Set, WeakMap, WeakSet (8) vector Set, the data in SIMD vector Set will be organized into a data sequence. For example, SIMD and other (9) structured data, these objects are used to represent and manipulate structured buffer data, or using JSON encoded data. For example, JSON, etc. (10) control abstract objects such as Promise and Generator, etc. (11) Reflection, such as Reflect and Proxy, etc. (12) Internationalization, in order to support multi-language processing, ECMAScript objects are added. Such as Intl, intl.collator, etc. (13) WebAssembly (14) others such as ArgumentsCopy the code

For more information, see the Classification of Standard Built-in Objects.

All built-in object properties and methods

4. 2. Undefined and declared

A variable that has been declared in scope but not yet assigned is undefined. In contrast, variables that have not been declared in the scope are undeclared.

For references to undeclared variables, the browser will report a ReferenceError, such as ReferenceError: b is not defined. However, we can use tyP Eof’s security mechanisms to avoid errors because typeof returns “undefined” for undeclared (or not defined) variables.

7. What is the difference between null and undefined?

First of all, Undefined and Null are both basic data types. Each of these basic data types has only one value, that is, Undefined and Null.

Undefined means undefined, and null means an empty object (not a real object, see below!). . Normal variables that are declared but not yet defined will return undefined. Null is primarily used to assign values to variables that may return objects as initialization.

In fact, null is not an object, although Typeof NULL will print object, but this is a long-standing Bug in JS. In the original version of JS, the 32-bit system was used, and for performance considerations, the type information of variables was stored in low order. The beginning of 000 represents an object, whereas null represents all zeros, so it was wrongly identified as object. Although the current internal type determination code has changed, this Bug has persisted.Copy the code

Undefined is not a reserved word in JS, which means we can use undefined as a variable name, which is dangerous because it can affect how we judge the value of undefined. But there are ways to get a safe undefined value, such as void 0.

Null typing returns “object” when we use typeof for both types, which is a historical problem. Return true when we compare two types of values with double equals, and false when we compare three equals.

For more information, please refer to:

Null and Undefined in JavaScript

8. What is the result of valueOf and toString for {} and []?

ValueOf = {}; toString = "[object object]"; []; toString = ""Copy the code

9. Scope and scope chains of Javascript

Scope: A scope is the area where variables are defined. It has a set of rules for accessing variables that govern how the browser engine looks for variables based on variables (identifiers) in the current scope and in nested scopes.

Scope chain: The purpose of the scope chain is to ensure orderly access to all variables and functions that the execution environment has access to. Through the scope chain, we can access variables and functions in the outer environment.

The scope chain is essentially a list of Pointers to variable objects. A variable object is an object that contains all the variables and functions in the execution environment. The front end of the scope chain is always a variable object for the current execution context. The variable object of the global execution context (that is, the global object) is always the last object in the scope chain.

When we look for a variable, we can look backward down the scope chain if it is not found in the current execution environment.

The process of creating a scope chain is related to the establishment of an execution context….

For more information, see the scope Chain for JavaScript In-depth Understanding.

Also check out my article “Front End Material Packages” that delves into JavaScript scope (chain) knowledge and closures

10. How many ways can javascript create objects?

We usually use literals to create objects directly, but this creates a lot of duplicate code when creating a large number of similar objects. But unlike ordinary object-oriented languages, JS had no concept of classes before ES6. However, we can use functions to simulate, so as to produce reusable object creation methods. There are several ways I know: (1) The first is the factory pattern. The main working principle of the factory pattern is to use functions to encapsulate the details of creating objects, so as to achieve the purpose of reuse by calling functions. But it has a big problem with creating objects that cannot be associated with a type. It simply encapsulates reusable code without establishing a relationship between the object and the type. (2) The second is the constructor pattern. Every function in js can be a constructor, as long as a function is called by new, then we can call it a constructor. The constructor first creates an object, then points the object's prototype to the constructor's prototype property, then points this in the execution context to the object, and finally executes the entire function. If the return value is not an object, the new object is returned. Since the value of this refers to the newly created object, we can assign a value to the object using this. The advantage of the constructor pattern over the factory pattern is that the object created is linked to the constructor, so we can identify the type of the object through the prototype. But the constructor, a weakness is caused unnecessary function object is created, because in js function is an object, so if the object attribute contains the function, so every time we will create a new function object, wasted unnecessary memory space, because the function is common to all instances. (3) The third mode is the prototype mode, because each function has a Prototype property. This property is an object that contains properties and methods shared by all instances created by the constructor. So we can use prototype objects to add common properties and methods, thereby reusing code. This approach solves the reuse problem of function objects compared to the constructor pattern. However, there are some problems with this pattern. One is that there is no way to initialize values by passing in parameters. The other is that if there is a value of a reference type such as Array, then all instances will share an object, and any change made by one instance to the value of the reference type will affect all instances. (4) The fourth pattern is the combination of the constructor pattern and the prototype pattern, which is the most common way to create custom types. Because both the constructor and prototype patterns are problematic when used separately, we can combine the two patterns, using constructors to initialize object properties and using prototype objects to reuse function methods. This approach does a good job of addressing the disadvantages of using the two patterns alone, but one disadvantage is that it does not encapsulate the code well enough because it uses two different patterns. (5) The fifth mode is the dynamic prototype mode, which moves the creation process of the prototype method assignment to the inside of the constructor. By judging whether the attribute exists, the effect of assigning a value to the prototype object can be realized only once when the function is called for the first time. This approach nicely encapsulates the blend mode above. (6) The sixth pattern is the parasitic constructor pattern, which is implemented in the same way as the factory pattern. My understanding of this pattern is that it is mainly based on an existing type and extends the instantiated object when instantiated. This extends the object without modifying the original constructor. One drawback is that, like the factory pattern, object recognition is not possible. Well, those are the ones I've seen so far.Copy the code

For more information, see “Object Creation with JavaScript in Depth”.

11. How many ways to implement JavaScript inheritance?

I know several ways to implement inheritance in JS: (1) The first way is to implement inheritance in the way of prototype chain, but the disadvantage of this way of implementation is that when the data containing reference type is shared by all instance objects, it is easy to cause confusion of modification. Also, you cannot pass parameters to a supertype when creating a subtype. (2) The second method is to use the borrowed constructor method. This method is implemented by calling the supertype constructor in the subtype function. This method solves the disadvantages of not passing parameters to the supertype, but it has the problem of function method reuse. And the method subtypes defined by the supertype prototype are not accessible. (3) The third approach is composite inheritance, which is a way of combining prototype chains and borrowed constructors. Inheritance of properties of types is achieved by borrowing constructors, and inheritance of methods is achieved by setting archetypes of subtypes to instances of supertypes. This approach solves the problem of using the two patterns separately, but since we prototyped the subtype using an instance of the supertype, we called the constructor of the superclass twice, resulting in a lot of unnecessary attributes in the subtype stereotype. (4) The fourth way is the original type inheritance. The main idea of the original type inheritance is to create a new object based on the existing object. The principle of implementation is to pass an object into the function, and then return an object based on this object as the prototype. The idea of inheritance is not to create a new type, but to achieve a simple inheritance of an Object. The object.create () method defined in ES5 is the implementation of the original type inheritance. The drawbacks are the same as the prototype chain approach. (5) The fifth way is parasitic inheritance. The idea of parasitic inheritance is to create a function that encapsulates the inheritance process by passing in an object, then making a copy of the object, then extending the object, and finally returning the object. This extension process can be understood as inheritance. The advantage of this inheritance is that it is implemented on a simple object, if the object is not of our custom type. The disadvantage is that there is no way to reuse functions. (6) The sixth approach is parasitic composite inheritance, which has the disadvantage of using an instance of a supertype as a prototype for a subtype, resulting in the addition of unnecessary stereotype attributes. Parasitic combination inheritance uses a copy of the stereotype of the supertype as the stereotype of the subtype, thus avoiding the creation of unnecessary attributes.Copy the code

For more information, see inheritance in JavaScript In-depth Understanding.

12. Implementation of parasitic composite inheritance?

function Person(name) {
  this.name = name;
}

Person.prototype.sayName = function() {
  console.log("My name is " + this.name + ".");
};

function Student(name, grade) {
  Person.call(this, name);
  this.grade = grade;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Student.prototype.sayMyGrade = function() {
  console.log("My grade is " + this.grade + ".");
  
};
Copy the code

Talk about your understanding of this, call, apply and bind

For more details, see my previous article “Front End Packages” for a thorough understanding of this, call, apply, and bind in JavaScript

  1. In the browser, this points to the window object globally;
  2. In functions, this always refers to the object that last called it;
  3. In the constructor, this refers to the new object that comes out of new;
  4. This in call, apply, and bind is strongly bound to the specified object.
  5. In the case of the arrow function this is special. The arrow function this is the parent scope of this, not the called this. The first four methods, you know, are defined when you call them, which is dynamic, whereas this is defined when you declare it;
  6. Apply, call, and bind are all built-in apis for javascript functions. Calls to them can specify the execution of this to the function and also pass arguments to it.

14. JavaScript prototypes, prototype chains? What are the characteristics?

In js, we use constructors to create new objects. Each constructor has a Prototype property value inside it. This property value is an object that contains properties and methods that can be shared by all instances of the constructor. When we create an object using the constructor, the object will contain a pointer to the value of the constructor’s prototype property. In ES5, this pointer is called the object’s prototype. Normally we shouldn’t be able to get this value, but browsers now implement the proto property to allow us to access this property, but it’s best not to use this property because it’s not specified in the specification. ES5 has added an object.getPrototypeof () method that allows us to get the prototype of an image.

When we access a property of an object, if the property doesn’t exist inside the object, then it will look for that property in its prototype object, and that prototype object will have its own prototype, and so on and so forth, the concept of a chain of prototypes. The end of the prototype chain is usually object.prototype, so that’s why new objects can use methods like toString().

Features:

JavaScript objects are passed by reference, and each new object entity we create does not have its own copy of the prototype. When we modify the prototype, the objects associated with it inherit the change.

Reference article:

Prototypes and Prototype Chains for Deep Understanding of JavaScript

Also check out what I’ve written: “Front End Packaging” for an in-depth understanding of JavaScript prototypes and prototype chains

How does js get the prototype?

  • p.proto
  • p.constructor.prototype
  • Object.getPrototypeOf(p)

16. What are closures and why use them?

A closure is a function that has access to variables in the scope of another function. The most common way to create a closure is to create another function within a function that has access to variables local to the current function.

Closures have two common uses.

  • The first use of closures is to enable us to access variables inside the function from outside. By using closures, we can access variables inside the function externally by calling the closure function externally. We can use this method to create private variables.
  • Another use of functions is to keep a variable object in the context of a function that has finished running in memory, because the closure function retains a reference to the variable object, so the variable object is not reclaimed.
function a(){ var n = 0; function add(){ n++; console.log(n); } return add; } var a1 = a(); // Note that the function name is only an identifier (a pointer to the function), and () is the execution function; a1(); //1 a1(); //2 The second call to n is still in memoryCopy the code

The nature of closures is a special application of scope chains. Once you know how to create a scope chain, you can understand how closures are implemented.

17. What are DOM and BOM?

DOM refers to the Document Object model, which refers to treating a document as an object that defines methods and interfaces for handling web content.

BOM refers to the Browser Object model. It refers to treating the browser as an object that defines the methods and interfaces to interact with the browser. The core of the BOM is the window, and the Window object has the dual role of being both an interface to the browser window through JS and a Global object. This means that any object, variable, or function defined in a web page exists as a property or method of the global object. Window objects include locati on objects, Navigator objects, screen objects, and other subobjects. The document object, the most fundamental DOM object, is also a subobject of the BOM window object.

Related information:

What’s the Difference between DOM, DOCUMENT, BOM, WINDOW?

Window Object

What are DOM and BOM, and how are they related?

JavaScript Learning Summary (3) BOM and DOM Details

18. What are the three event models?

An event is an interactive action that occurs when a user operates a web page or some operations of the web page itself. Modern browsers have three kinds of event models.

  1. Dom0-level model: this model does not propagate, so there is no concept of event flow, but some browsers now support bubbling implementations, which can define the listener directly in the web page, or specify the listener via js properties. This approach is compatible with all browsers.
  2. IE event model: In this event model, an event has two processes, event processing phase and event bubbling phase. The event processing phase first executes the listener event bound to the target element. Then there is the event bubbling phase, which refers to the event bubbling from the target element to the Document, checking whether the passing node is bound to the event listener function, and executing if so. This model adds listening functions through attachEvent, and you can add multiple listening functions that are executed in sequence.
  3. Dom2-level event model: In this event model, an event has three processes, the first of which is the event capture phase. Capture refers to the event propagating from the Document all the way down to the target element, checking in turn whether the passing node is bound to the event listener function, and executing if so. The latter two phases are the same as the two phases of the IE event model. In this event model, the function bound to the event is addEventListener, where the third parameter specifies whether the event is executed during the capture phase.

Related information:

When a DOM element is bound to multiple events, Bubble or Capture first

19. What is event delegation?

Event delegation essentially takes advantage of the browser event bubbling mechanism. Since events are uploaded to the parent node during the bubbling process, and the parent node can obtain the target node through the event object, the listening function of the child node can be defined on the parent node, and the listening function of the parent node can handle the events of multiple child elements uniformly, which is called event proxy.

Using an event agent we can reduce memory consumption by not binding a listener event to each child element. For example, if a new child node is added, we do not need to add a listener event to it. The listener function in the parent element will handle the event.

Related information:

JavaScript Event Delegation in Detail

20. What is event transmission?

When an event occurs on a DOM element, the event does not occur entirely on that element. When an event occurs on a DOM element, the event does not occur entirely on that element.

There are three stages of event propagation:

  1. Capture phase – Events start at the window and work their way down to each element until the target element event or event.target is reached.
  2. Target phase – The event has reached the target element.
  3. Bubble phase – Events bubble from the target element and then up to each element until they reach the window.

21. What is event capture?

When an event occurs on a DOM element, the event does not occur entirely on that element. In the capture phase, events start at the window and go all the way to the element that triggered the event. Window – > document — — — — — — — — > > HTML body — — — – > target element

Suppose you have the following HTML structure:

<div class="grandparent">
  <div class="parent">
    <div class="child">1</div>
  </div>
</div>
Copy the code

The corresponding JS code:

function addEvent(el, event, callback, isCapture = false) { if (! el || ! event || ! callback || typeof callback ! == '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 an optional third argument, useCapture, which defaults to false, and the event will occur during the bubble phase or, if true, during the capture phase. If you click on the Child element, it prints window, Document, HTML, GrandParent, and Parent on the console, respectively, which is called event capture.

22. What is an event bubble?

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

Suppose you have the following HTML structure:

<div class="grandparent">
  <div class="parent">
    <div class="child">1</div>
  </div>
</div>
Copy the code

The corresponding JS code:

function addEvent(el, event, callback, isCapture = false) { if (! el || ! event || ! callback || typeof callback ! == '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 an optional third argument, useCapture, which defaults to false, and the event will 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 on the console, respectively, which is called event bubbling.

23. DOM operations — How do I add, remove, move, replicate, 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 operations

getAttribute(key);
setAttribute(key, value);
hasAttribute(key);
removeAttribute(key);
Copy the code

Related information:

Overview of DOM

Native JavaScript DOM Manipulation summary

Native JS DOM Node API Collection

24. What are the native methods of js arrays and strings

25. Common regular expressions (only for collection, not deep involved)

/ / (1) matching hex color value var color = / # ([0-9 a - fA - F] {6} | [0-9 a - fA - F] {3})/g; / / (2) the match date, such as yyyy - mm - dd format var date = / ^ [0-9] {4} - (0 [1-9] | [0-2] 1) - (0 | [1-9] [12] [0-9] [01]) | 3 $/; Var qq = /^[1-9][0-9]{4,10}$/g; Var phone = /^1[34578]\d{9}$/g; var phone = /^1[34578]\d{9}$/g; / / regular var (5) the user name/username = ^ [a zA - Z \ $] [a zA - Z0 - _ \ $9] 16th {4} $/; / / (6) Email regular var Email = / ^ ([A - Za - z0-9 _ \ - \]) + \ @ ([A - Za - z0-9 _ \ - \]) + \. ([A Za - z] {2, 4}) $/; // (7) id number (18 digits) regular var cP = /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/; Var urlP= /^((HTTPS? |ftp|file):\/\/)? ([\ \. Da - z -] +) \. ([a-z \.] {2, 6}) (/ \ \ / \ w - *) * \ /? $/; Var ipP = /^(? : (? : 25 [0 to 5] | 2 [0 to 4] [0-9] | [01]? [0-9] [0-9]?) \.) {3} (? : 25 [0 to 5] | 2 [0 to 4] [0-9] | [01]? [0-9] [0-9]?) $/; / / (10) / / license number regular var cPattern = / ^ [beijing-tianjin Shanghai yu ji yu cloud liao black xiang anhui new GuiGan of hubei province can Sue your yue jin meng shan ji min qinghai-tibet plain NingQiong that brought A - Z] {1} {1} [a-z] [A - Z0-9] {4} [A - Z0-9 hang cop Hong Kong and Macao] {1} $/; // (11) Strong password (8-10 in length) : var PWD = /^(? =.\d)(? =.[a-z])(? =. [a-z]). 8, 10 {} $/Copy the code

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

My understanding of Ajax is that it is a method of asynchronous communication, by sending HTTP traffic directly to the server from a JS script, and then updating the corresponding part of a web page based on the data returned by the server, rather than refreshing the entire page.

Creation steps:

Handwritten interview (native) :

Var XHR = window.xmlHttprequest? new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP'); Xhr. open('get','index.xml',true); //3: send the request xhr.send(null); //4: Listening request, Accept 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: true,//async asynchronous sync data:data,// dataType:'jsonp', success:function (msg) { }, error:function (error) { } })Copy the code

Promise encapsulation implementation:

// Promise package implementation: Function getJSON(url) {let promise = new Promise(function(resolve, reject) { let xhr = new XMLHttpRequest(); // Create a new HTTP request xhr.open("GET", URL, true); Xhr.onreadystatechange = function() {if (this.readyState! == 4) return; If (this.status === 200) {resolve(this.response); if (this.status === 200) {resolve(this.response); } else { reject(new Error(this.statusText)); }}; Xhr. onError = function() {reject(new Error(this.statustext)); }; // Set the data type of the response xhr.responseType = "json"; Xhr. setRequestHeader("Accept", "application/json"); // Send an HTTP request xhr.send(null); }); return promise; }Copy the code

27. What are the methods of js lazy loading?

The loading, parsing and execution of JS will block the page rendering process, so we hope that the JS script can be loaded lazily as much as possible to improve the page rendering speed.

Several 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 js script defer property, which will cause the loading of the script to be parsed in sync with the parsing of the document, and then execute the script file after the document is parsed, so that the rendering of the page is not blocked. Multiple scripts that set the defer property are executed last in order by specification, but this may not be the case in some browsers.
  3. Add async property to js script. This property will make the script load asynchronously and will not block the page parsing process. However, the js script will be executed immediately after the script is loaded, and it will also block if the document is not parsed. Scripts with multiple async properties have unpredictable execution orders and generally do not execute in the order of the code.
  4. By dynamically creating the DOM tag, we can listen to the loading event of the document, and then dynamically create the script tag to introduce the JS script when the document is loaded.

Related information:

How to use JS lazy loading

The HTML 5<script> asyncAttribute”

28. What is your understanding of modular development?

My understanding of a module is that a module is a set of methods to implement a particular function. At the beginning, JS only implemented 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 original writing method is to use functions as modules, several functions as a module, but this way is easy to cause global variable contamination, and the modules have no connection.

The object writing approach is presented later, which solves some of the disadvantages of using functions as modules directly by implementing the function as an object method, but this approach exposes all the module members, and external code can modify the values of internal attributes.

The most common approach is to write functions that execute immediately, using closures to create the private scope of a module without polluting the global scope.

Related materials: “Discussion on Modular Development”

Javascript Modular Programming (PART 1) : How to Write modules

Front End Modularity: CommonJS, AMD, CMD, ES6

Syntax for Module

29. How many module specifications of JS?

There are four mature module loading schemes in JS:

  • The first is the CommonJS solution, which introduces the module through require and defines the module’s output interface through module.exports. This module loading scheme is a server-side solution. It introduces modules in a synchronous way, because on the server the files are stored on the local disk, so the reading is very fast, so there is no problem with loading the modules in a synchronous way. On the browser side, however, asynchronous loading is more appropriate because the module is loaded using network requests.
  • The second is the AMD scheme, which uses the mode of asynchronous loading to load the module. The loading of the module does not affect the execution of subsequent 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 that dependencies are handled differently when modules are defined and when dependent modules are executed.
  • The fourth option, proposed by ES6, uses the form of import and export to import and export modules.

30. What are the differences between AMD and CMD specifications?

The main differences are twofold.

  1. The first aspect is how dependencies are handled differently at module definition time. AMD is a big fan of dependency prepositioning, where modules are declared when they are defined. CMD, on the other hand, favors proximity, requiring only when a module is needed.
  2. The second aspect is that the execution timing of dependent modules is handled differently. Both AMD and CMD load modules asynchronously, but here’s the difference

When a module is executed, AMD directly executes the dependent module after it is loaded. The execution order of the dependent module may not be consistent with the order we write. CMD, on the other hand, does not execute the dependent modules after they are loaded. It just downloads the modules. After all the dependent modules are loaded, it enters the callback function logic and executes the corresponding modules only when the require statement is encountered, so that the execution order of the modules is consistent with the order in which we write them.

// CMD
define(function(require.exports.module) {
  var a = require("./a");
  a.doSomething();
  // Omit 100 lines here
  var b = require("./b"); Dependencies can be written nearby
  b.doSomething();
  // ...
});

// AMD's default recommendation
define(["./a"."./b"].function(a, b) {
  Dependencies must be written from the start
  a.doSomething();
  // Omit 100 lines here
  b.doSomething();
  // ...
});
Copy the code

Related information:

Front End Modularity: The Difference between AMD and CMD

31. Differences between ES6 module and CommonJS module, AMD and CMD.

  • 1.CommonJSThe module outputs a copy of the value. The ES6 module outputs a reference to the value.CommonJSThe module outputs values

In other words, once a value is output, changes inside the module cannot affect the value. The ES6 module operates differently from CommonJS. When the JS engine statically analyzes the script, it generates a read-only reference when it encounters the module loading command import. When the script actually executes, the value is then applied to the loaded module based on the read-only reference.

  • 2.CommonJSModules are runtime loads, and ES6 modules are compile-time output interfaces.CommonJSA module is an object, that is, when input is loaded the entire module, an object is generated, and then the method is read from the object. This kind of loading is called “runtime loading”. While an ES6 module is not an object, its external interface is just a static definition, which is generated during the static code parsing phase.

32. What are the core principles of requireJS?

The core principle of require.js is to introduce modules asynchronously by dynamically creating script scripts, and then listen for the load event of each script. If each script is loaded, then call the callback function. ` ` `

For more information, see requireJS: An Analysis of Usage and Principles.

What are the core principles of requireJS?

RequireJS Principle Analysis

33. Talk about the operating mechanism of JS

1. Js single thread

One of the characteristics of the JavaScript language is that it’s single-threaded, which means you can only do one thing at a time.

JavaScript is single threaded, depending on what it’s used for. As a browser scripting language, JavaScript’s primary use is to interact with users and manipulate the DOM. This determines that it has to be single threaded, otherwise it will cause complex synchronization issues. For example, if JavaScript has two threads at the same time, one adding content to a DOM node and the other removing the node, which thread should the browser use?

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

2. Js event loop

There are many tasks in the execution of JS code. These tasks fall into two categories:

  • Synchronization task
  • Asynchronous tasks

When we open a website, the rendering process is a bunch of synchronization tasks, such as rendering the skeleton of the page and the elements of the page. Tasks that take up a lot of resources, such as loading pictures and music, are asynchronous. , we use a map to illustrate:Let’s explain this picture:

  • Synchronous and asynchronous tasks enter different execution “sites”, synchronously into the main thread and asynchronously into the main threadEvent TableAnd register the function.
  • When the specified thing is done,Event TableI’m going to move this function inEvent Queue.
  • Tasks in the main thread are empty after execution and will goEvent QueueRead the corresponding function, enter the main thread execution.
  • The process is repeated over and over againEvent Loop(Event loop).

When is the main thread execution stack empty? The JS engine has a monitoring process that continuously checks to see if the main thread execution stack is empty. Once empty, it checks the Event Queue to see if any functions are waiting to be called.

Above is the overall process of JS running

Note that in addition to synchronous and asynchronous tasks, tasks can be further subdivided into macroTasks and microtasks, with the JS engine giving priority to the microtasks

Microtasks include callbacks for promises, process.nextTick in Node, and MutationObserver that listens for Dom changes. Macro tasks include the execution of script scripts, setTimeout, setInterval, setImmediate and other timed events, as well as I/O operations, UI rendering and so on.Copy the code

How should you respond in an interview? Here are my recommended responses:

  1. First of all, JS is run in 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 its return. Instead, it suspends the event and continues to execute other tasks in the execution stack
  3. After the synchronous event is executed, the callback corresponding to the asynchronous event is added to another task queue different from the current execution stack to wait for execution.
  4. Task queue can be divided into macro task to column and microtask to column, when the current execution stack of the event execution is completed, JS engine will first determine whether there are tasks in the microtask to column can be executed, if there is, the first event of the microtask queue will be pushed to the stack for execution.
  5. When all tasks in the column are executed by the microtask, the macro task is judged to be the task in the column.

Finally, you can test the results with the following question:

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 to execute, meets setTimeout, throws the setTimeout callback function into the macro task queue, executes new Promise immediately, outputs 2, then throws the callback function into the microtask queue, continues to execute, meets process. When all the synchronized tasks are finished, check whether there are any microtasks that can be executed. There are two microtasks: then function and nextTick function. Which one should be executed first? The asynchronous task specified by process.nextTick always occurs before all asynchronous tasks, so the first round of execution is completed by executing process.nextTick output 4 and then function output 3. Round 2: start from macro task queue, find setTimeout callback, output 1 finished, so the result is 25431

Related information:

Browser Event Loop

The Event Loop mechanism in JavaScript

What is an Event Loop?

This Time, Understand JavaScript Execution Thoroughly

34. What is the object of Arguments?

The Arguments object is a collection of parameter values passed in the function. It is an array-like object because it has a length attribute, and we can use array index notation Arguments [1] to access a single value, but it does not have the built-in methods in arrays such as forEach, Reduce, Filter, and map.

We can use array.prototype. slice to convert the Arguments object into an Array.

function one() {
  return Array.prototype.slice.call(arguments);
}
Copy the code

Note: There is no arguments object in the arrow function.

function one() {
  return arguments;
}
const two = function () {
  return arguments;
}
const three = function three() {
  return arguments;
}

const four = () = > arguments;

four(); // Throws an error - arguments is not defined
Copy the code

When we call function four, it throws a ReferenceError: Arguments is not defined error. Using REST syntax, you can solve this problem.

const four = (. args) = > args;
Copy the code

This automatically puts all parameter values into the array.

35. Why is this function called in the codebIt’s going to be a global variable, right?

function myFunc() {
  let a = b = 0;
}

myFunc();
Copy the code

The reason is that the assignment operator evaluates from right to left. This means that when multiple assignment operators appear in an expression, they are evaluated from right to left. So the above code looks like this:

function myFunc() {
  let a = (b = 0);
}

myFunc();
Copy the code

First, the expression B = 0 is evaluated, and b is not declared in this example. Therefore, the JS engine creates a global variable b outside of this function, and then the expression b = 0 returns a value of 0 and assigns a new local variable a.

We can solve this problem by declaring variables before assigning.

function myFunc() {
  let a,b;
  a = b = 0;
}
myFunc();
Copy the code

36. A brief introduction to the V8 garbage collection mechanism

V8's garbage collection mechanism is based on generational collection, which in turn is based on the generation hypothesis, which is characterized by the fact that newly born objects tend to die early and that undead objects live longer. Based on this hypothesis, V8 engines divide memory into old and new generations. Newly created objects or objects that have only been garbage collected once are called the new generation. Objects that have undergone multiple garbage collections are called old generations. The new generation is divided into two Spaces: "From" and "To". Scavenge is applied when the From space is full. The application logic will stop when we run the garbage collection algorithm, and continue when the garbage collection is complete. This algorithm is divided into three steps: (1) First, the surviving objects of the From space are checked. If the objects survive, it is determined whether the objects meet the conditions for promotion to the old generation. If the conditions are met, it is promoted to the old generation. If the condition is not met 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 are swapped. Be insane. There are two conditions for a Cenozoic object to be promoted to the old generation. (1) The first is to judge whether the object has gone through a Scavenge. If experienced, the object is copied From the From space to the old generation; If no experience, copy To the To space. (2) The second is whether the memory usage of To space exceeds the limit. When an object is copied From the From space To the To space, if the To space usage exceeds 25%, the object is directly promoted To the old generation. The main reason for setting 25% is that after the algorithm ends, the two Spaces will be swapped. If the memory size of the To space is too small, the subsequent memory allocation will be affected. In the old generation, label elimination method and label compression method were adopted. Tag removal first marks the objects that are alive in memory, and then clears those that are not. Since the mark will cause a lot of memory fragmentation, it is not convenient for memory allocation. Therefore, to solve the problem of memory fragmentation, the tag compression method is introduced. Since the logic of the application is paused during the garbage collection, each pause is not very long for the younger generation method because of the small memory, but for the older generation each garbage collection takes a long time, and the pause can have a significant impact. To solve this problem V8 introduced the incremental marking method, which breaks a single pause into multiple steps, alternating one small step at a time with the running logic running for a while.Copy the code

Related information:

Understanding how V8 garbage collection works

Garbage Collection in JavaScript

37. What operations cause memory leaks?

  • 1. Unexpected global variables
  • 2. Forgotten timers or callbacks
  • 3. Detach references from the DOM
  • 4. The closure
  • In the first case, we accidentally create a global variable by using an undeclared variable, leaving it in memory unrecyclable.
  • The second case is we setsetIntervalTimer, and forget to cancel it. If the loop function has a reference to an external variable, the variable will remain in memory and cannot be recycled.
  • In the third case, we get a reference to a DOM element, and then the element is deleted, and since we keep the reference to the element, it cannot be reclaimed.
  • The fourth is the unreasonable use of closures, which causes some variables to remain in memory.

Related information:

JavaScript Memory Leaks

JavaScript Memory Leaks of Class 4 and How to Avoid them

“Prevent the occurrence of four types of memory leakage in JS”

Typical javascript memory Leaks and How to Troubleshoot Chrome

The following 38 to 46 items are the basics of ECMAScript 2015(ES6)Copy the code

38. What is ECMAScript?

ECMAScript is the standard for writing scripting languages, which means that JavaScript follows the specification changes in the ECMAScript standard because it is the blueprint for JavaScript.

ECMAScript and Javascript are both essentially related to the same language, the name of the language and the constraints of the language. It’s just that the person who invented Javascript (Netscape), I gave it to ECMA (European Computer Manufacturers Association), and this guy stipulated his standards, because there was The Java language at that time, and he wanted to emphasize that this thing was made by this guy ECMA, so this amazing thing was born. The name of this thing is ECMAScript.

JavaScript = ECMAScript + DOM + BOM

JavaScript does what ECMAScript says!

JavaScript (narrow JavaScript) does everything by asking ECMAScript if I can do it! I’d be wrong if I couldn’t! I’m right if I can!

— Suddenly I feel JavaScript has no dignity, why do I need someone to restrain me?

That person was created or wronged, but he was created solely to constrain JavaScript.

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

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

40. var.letandconstWhat’s the difference?

Variables declared by var are mounted on window, 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

Var declares that variables are promoted, let and const do not:

console.log(a); // undefined ===> a has declared no value, and undefined by default
var a = 100;

console.log(b); // Error: b is not defined ===> The variable b cannot be found
let b = 10;

console.log(c); // Error: c is not defined ===> The variable c cannot be found
const c = 10;
Copy the code

Let and const declarations form the block scope


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

console.log(a); / / 100
console.log(b)  // Error: b is not defined ===> The variable b cannot be found

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

if(1) {var a = 100;
  const c = 1;
}
console.log(a); / / 100
console.log(c)  // Error: c is not defined ===> The variable c cannot be found
Copy the code

Let and const cannot declare variables of the same name in the same scope. Var can

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

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

Declare ===> declared ===>
Copy the code

Temporary dead zone

var a = 100;

if(1){
    a = 10;
    // If a is declared let/const in the current scope, if a is assigned 10, then only a is found in the current scope.
    // Error:a is not defined
    let a = 1;
}
Copy the code

const


/* * * * * * * * * * * * * * * * If you declare a complex data type, you can modify its attribute * * */

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

41. What is an arrow function?

Arrow function expressions have a more concise syntax than function expressions and do not have their own this, arguments, super, or new.target. The arrow function expression is more suitable for places where anonymous functions would otherwise be required, 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 required 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 an 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 in 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

The arrow function cannot access the Arguments object. So calling the first getArgs will throw an error. Instead, we can use rest parameters to get all the parameters passed in the arrow function.

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

The arrow function does not have its own this value. It captures the this value of the lexical scope function. In this example, the addAll function will copy the this value from the computeResult method, which would be the Window object if we declared the arrow function at the global scope.

42. What is a class?

Class is a new way to write constructors in JS. It is syntactic sugar for using constructors, which are still prototypes and prototype-based inheritance at the bottom.

 //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

Overrides methods and inherits 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 prototypes 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

Related information:

ECMAScript 6 implements classes. What does it mean for JavaScript front-end development?

The Basic Grammar of Class

What is a template string?

Template strings are a new way to create strings in JS. We can string the template 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 the multi-line effect. In template strings we don’t need this trouble:

//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 the template string, 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 want to add an expression or value to a string, you need to use the + operator. In the template string S, we can use ${expr} to embed an expression, which makes it cleaner than the ES5 version.

44. What is object deconstruction?

Object destruction is a new, more concise method of getting or extracting 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, an early method was to create a variable with the same name as the object’s property. This method is cumbersome because we create a new variable for each property. Let’s say we have a large object, and it has a lot of properties and methods, and extracting the properties in this way would be cumbersome.

var firstName = employee.firstName;
var lastName = employee.lastName;
var position = employee.position;
var yearHired = employee.yearHired;
Copy the code

Using deconstructive syntax becomes much simpler:

{ firstName, lastName, position, yearHired } = employee;
Copy the code

We can also alias properties:

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

If the value is undefined, we can also specify a default value:

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

45. What isSetObject, how does it work?

Set objects allow you to store unique values of any type, whether raw values or object references.

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, and since the add method returns a Set object, we can use add again in a chained fashion. If a value already exists in a Set, it will no longer be added.

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

We can use the HAS method to check whether a particular value exists in the 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 in 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

There is also a WeakSet, similar to a Set, which is a Set of non-repeating values. But a member of a WeakSet can only be an object, not a value of any other type. All objects in a WeakSet are weak references, that is, the garbage collection mechanism does not consider the reference of the object by a WeakSet.

  • Map Data structure. It is similar to an object in that it is also a collection of key-value pairs, but the range of “keys” is not limited to strings, and all types of values (including objects) can be used as keys.

  • Similar to the Map structure, the WeakMap structure is also used to generate a collection of key and 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 pointed to by the key name of a WeakMap is not included in the garbage collection mechanism.

46. What is a Proxy?

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

Proxy can be understood as that a layer of “interception” is set up before the target object, and the external access to the object must pass this layer of interception first. Therefore, it provides a mechanism to filter and rewrite the external access. The original meaning of the word Proxy is a Proxy, used here to mean that it “proxies” certain operations.

High warning ⚡⚡⚡, the following 47~64 is more difficult in JavaScript advanced knowledge and related handwritten implementation, you need to slowly detailedCopy the code

47. Write a generic event listener function

Const EventUtils = {/ / sight) respectively using dom0 | | dom2 | | IE way to bind / / add events addEvent: function(element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; }}, // remove the event removeEvent: function(element, type, handler) { if (element.removeEventListener) { element.removeEventListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent("on" + type, handler); } else { element["on" + type] = null; }}, / / to get event target getTarget: function (event) {return event. The target | | event. The srcElement; }, / / the event object for reference, take all the information to the event, to ensure that can be used at any time the event getEvent: function (event) {return event | | window. The event; }, // Stop events (mainly event bubble, because IE does not support event capture) stopPropagation: function(event) { if (event.stopPropagation) { event.stopPropagation(); } else { event.cancelBubble = true; }}, // preventDefault: function(event) {if (event.preventdefault) {event.preventdefault (); } else { event.returnValue = false; }}};Copy the code

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

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

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

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

49. 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

50. Why are functions called first-class citizens?

In JavaScript, functions not only behave like traditional functions (declared and called), but also act 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, a Function in JavaScript acts as a class constructor and is an instance of the Function class. This multiple identity makes JavaScript functions very important.

51. Implement manuallyArray. The prototype. The map method

The map() method creates a new array, and the result is the result of calling one of the provided functions for each element in the array.

function map(arr, mapCallback) {
  // First, check that the parameters passed are correct.
  if (!Array.isArray(arr) || ! arr.length ||typeofmapCallback ! = ='function') { 
    return [];
  } else {
    let result = [];
    // Each time we call this function, we create a result array
    // Because we don't want to change the original array.
    for (let i = 0, len = arr.length; i < len; i++) {
      result.push(mapCallback(arr[i], i, arr)); 
      // Push the result returned by mapCallback into the result array
    }
    returnresult; }}Copy the code

52. Implement manuallyArray.prototype.filtermethods

The filter() method creates a new array that contains all the elements that passed the tests implemented by the provided function.

function filter(arr, filterCallback) {
  // First, check that the parameters passed are correct.
  if (!Array.isArray(arr) || ! arr.length ||typeoffilterCallback ! = ='function') 
  {
    return [];
  } else {
    let result = [];
     // Each time we call this function, we create a result array
     // Because we don't want to change the original array.
    for (let i = 0, len = arr.length; i < len; i++) {
      // Check whether the return value of filterCallback is true
      if (filterCallback(arr[i], i, arr)) { 
      // If the condition is true, push the array elements into resultresult.push(arr[i]); }}return result; // return the result array}}Copy the code

53. Implement manuallyArray.prototype.reducemethods

The reduce() method executes a Reducer function (ascending) that you provide for each element in the array, summarizing its results into a single return value.


function reduce(arr, reduceCallback, initialValue) {
  // First, check that the parameters passed are correct.
  if (!Array.isArray(arr) || ! arr.length ||typeofreduceCallback ! = ='function') 
  {
    return [];
  } else {
    // If initialValue is not passed to the function, we will use the first array entry as the initialValue
    lethasInitialValue = initialValue ! = =undefined;
    let value = hasInitialValue ? initialValue : arr[0]; ,// If initialValue is passed, the index starts at 1, otherwise it starts at 0
    for (let i = hasInitialValue ? 1 : 0, len = arr.length; i < len; i++) {
      value = reduceCallback(value, arr[i], i, arr); 
    }
    returnvalue; }}Copy the code

54. A light and dark copy of js

It’s always difficult to write a deep copy of JavaScript, and if an interviewer asked me to write a deep copy right now, I’d probably just be able to write a basic version. So before I wrote this, I read the blog posts of all the big names in my favorites. For details, see the link I posted below. Here is a simple summary.

  • Shallow copy: Creates a new object that has an exact copy of the property values of the original object. If the property is of a primitive type, the value of the primitive type is copied, and if the property is of a reference type, the memory address is copied, so if one object changes the address, the other object is affected.
  • Deep copy: A complete copy of an object from the memory, from the heap memory to create a new area to store the new object, and the new object will not affect the original object.

Shallow copy implementation:

  • The object.assign () method: copies the values of all enumerable properties from one or more source objects to the target Object. It returns 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.
  • Extension operator.:
let a = {
    name: "Jake".flag: {
        title: "better day by day".time: "2020-05-31"}}letb = {... a};Copy the code

Deep copy implementation:

  • Beggar version: json.parse (json.stringify (object)) has many disadvantages (ignoring undefined, symbol, function; Cannot resolve circular references; Can’t handle regular, new Date())
  • Basic version: shallow copy + recursion (only the common 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

Reference article:

How to write a deep copy of an amazing interviewer

The ultimate search for deep copy (unknown to 99% of the population)

55. Write the call, apply, and bind functions

Call function implementation steps:

  • 1. Determine whether the calling object is a function. Even if we define it on the prototype of the function, it may be called by means such as call.
  • 2. Check whether the incoming context object exists. If it does not exist, set it to window.
  • 3. Process the passed parameters and intercept all the parameters after the first parameter.
  • 4. Make the function a property of the context object.
  • 5. Use the context object to call this method and save the returned result.
  • 6. Delete the newly added attribute.
  • 7. Return the result.
// Call function implementation
Function.prototype.myCall = function(context) {
  // Determine the calling object
  if (typeof this! = ="function") {
    console.error("type error");
  }

  // Get the parameter
  let args = [...arguments].slice(1),
    result = null;

  // Check whether the context was passed in. If not, set it to window
  context = context || window;

  // Sets the calling function as a method of an object
  context.fn = this;

  // Call the functionresult = context.fn(... args);// Delete the attribute
  delete context.fn;

  return result;
};
Copy the code

To implement the apply function:

    1. Determine if the calling object is a function, even if we are defining the prototype of the function, but it is possible to use the call method.
    1. Determines if the incoming context object exists, and if it does not, sets it to window.
    1. Take the function as a property of the context object.
    1. Determines whether the parameter value is passed in
    1. Use the context object to call this method and save the returned result.
    1. Delete the attributes you just added
    1. Returns the result

// Implement the apply function

Function.prototype.myApply = function(context) {
  // Determine whether the calling object is a function
  if (typeof this! = ="function") {
    throw new TypeError("Error");
  }

  let result = null;

  // Check whether the context exists, and if not, window
  context = context || window;

  // Sets functions as methods of objects
  context.fn = this;

  // Call the method
  if (arguments[1]) { result = context.fn(... arguments[1]);
  } else {
    result = context.fn();
  }

  // Delete the attribute
  delete context.fn;

  return result;
};


Copy the code

Bind (bind);

  • 1. Determine whether the calling object is a function. Even if we define it on the prototype of the function, it may be called by means such as call.
  • 2. Save the reference to the current function and get the values of the remaining parameters.
  • 3. Create a function return
  • 4. If you use apply internally to bind a function call, you need to determine if the function is used as a constructor. In this case, you need to pass the current function’s this to the apply call.
// Implement the bind function
Function.prototype.myBind = function(context) {
  // Determine whether the calling object is a function
  if (typeof this! = ="function") {
    throw new TypeError("Error");
  }

  // Get the parameter
  var args = [...arguments].slice(1),
    fn = this;

  return function Fn() {
    // Pass in different binding values depending on how the call is made
    return fn.apply(
      this instanceof Fn ? this: context, args.concat(... arguments) ); }; };Copy the code

Reference article: Handwritten Call, Apply, and Bind Functions

JavaScript In-depth Call and Apply simulation implementation

56. The implementation of function currying

Function currying refers to the technique of converting a function with multiple arguments into a series of functions with one argument.

function curry(fn, args) {
  // Get the length of the parameters required by the function
  let length = fn.length;

  args = args || [];

  return function() {
    let subArgs = args.slice(0);

    // Concatenate all existing parameters
    for (let i = 0; i < arguments.length; i++) {
      subArgs.push(arguments[i]);
    }

    // Determine whether the length of the parameter is sufficient for the function
    if (subArgs.length >= length) {
      // If so, execute the function
      return fn.apply(this, subArgs);
    } else {
      // If not, return the corilized function recursively and wait for the arguments to be passed
      return curry.call(this, fn, subArgs); }}; }/ / es6 implementation
function curry(fn, ... args) {
  returnfn.length <= args.length ? fn(... args) : curry.bind(null, fn, ... args); }Copy the code

Reference article: Function Currying in JavaScript topics

57. js emulates the implementation of the new operator

If you look at gold, you’ll probably get something like this:

To be honest, THE first time I saw it, I didn’t understand it. I had to go through prototypes and prototype chains to understand it. So I thinkMDNThe explanation for new is easier to understand:

The new operator creates an instance of a user-defined object type or an instance of a built-in object with a constructor. The new keyword does the following:

  1. Create an empty simple JavaScript object (that is, {});
  2. Link this object (that is, set its constructor) to another object;
  3. Use the newly created object in Step 1 as the context for this;
  4. If the function does not return an object, it returns this.

Next, let’s look at the implementation:

function Dog(name, color, age) {
  this.name = name;
  this.color = color;
  this.age = age;
}

Dog.prototype={
  getName: function() {
    return this.name
  }
}

var dog = new Dog('rhubarb'.'yellow'.3)

Copy the code

I believe the above code does not need to explain, everyone understands. Let’s look at the last line of code with the new keyword and follow steps 1,2,3, and 4 above to parse the operation behind the new.

Step 1: Create a simple empty object

var obj = {}
Copy the code

Step 2: Link this object to another object (prototype chain)

// Set the prototype chain
obj.__proto__ = Dog.prototype
Copy the code

Step 3: Use the newly created object from Step 1 as the context for this

// this points to an obj object
Dog.apply(obj, ['rhubarb'.'yellow'.3])
Copy the code

Step 4: If the function returns no object, return this

// Because Dog() does not return a value, obj is returned
var dog = obj
dog.getName() // 'rhubarb'
Copy the code

Note that Dog() returns the value of return if it has a return

var rtnObj = {}
function Dog(name, color, age) {
  // ...
  // Return an object
  return rtnObj
}

var dog = new Dog('rhubarb'.'yellow'.3)
console.log(dog === rtnObj) // true

Copy the code

Next we’ll wrap the above steps into an object instantiation method that emulates the operation of new:

function objectFactory(){
    var obj = {};
    // Get the first argument of the method (and remove the first argument), which is the constructor
    var Constructor = [].shift.apply(arguments);
    // Point the new object's internal property __proto__ to the constructor's prototype, so that the new object can access the properties and methods in the prototype
    obj.__proto__ = Constructor.prototype;
    // Get the return value of the constructor
    var ret = Constructor.apply(obj, arguments);
    // Return an object if the return value is an object, otherwise an instance of the constructor
    return typeof ret === "object" ? ret : obj;
}

Copy the code

58. What is a callback function? What are the drawbacks of callback functions

A callback function is an executable piece of code that is passed as an argument to other code so that it can be called when needed.

In JavaScript, a function is also an object. An object can be passed as an argument to a function, so a function can also be passed as an argument to another function. The function that takes an argument is called a callback function.

const btnAdd = document.getElementById('btnAdd');

btnAdd.addEventListener('click'.function clickCallback(e) {
    // do something useless
});
Copy the code

In this case, we wait for the Click event in the element with id btnAdd, and if it is clicked, the clickCallback function is executed. A callback function adds functionality to some data or event.

The Achilles heel of Callback functions is that they are easy to write Callback hell. Assume multiple events have dependencies:

setTimeout(() = > {
    console.log(1)
    setTimeout(() = > {
        console.log(2)
        setTimeout(() = > {
            console.log(3)},3000)},2000)},1000)
Copy the code

This is a typical callback hell. The above code looks bad to read and maintain, and it gets messy once there are too many events, so promises and async/await are introduced in ES6 to solve the callback hell problem. Of course, callbacks have several other disadvantages, such as the inability to catch an error using a try or catch, and the inability to return directly. The next two are designed to solve these problems. Let’s read on.

59. What is a Promise? Can I write it by hand?

It will give you a result over time. Programmatically, Promise is a solution to asynchronous programming. Here is the MDN for Promise:

A Promise object is a proxy object (which proxies a value) that may not be known when the Promise object is created. It allows you to attach handlers to the success and failure of asynchronous operations. This allows an asynchronous method to return a value just like a synchronous method, but instead of returning the final execution result immediately, a promise object represents a future result.

A Promise can have the following states:

  • Pending: Initial state, neither a success nor a failure state.
  • Fulfilled: indicates that the operation is completed successfully.
  • Rejected: Indicates that the operation fails.

This promise will never be changed once it has been changed from a waiting state to something else. This means that once the state becomes fulfilled/rejected, it cannot be changed again. If you don’t understand the concept, Promise is a simple one;

If I have a girlfriend, and it’s her birthday next Monday, and I promise her a surprise for her birthday, then from now on the promise goes into a waiting state, waiting for next Monday, and then the state changes. This is very depressing. If I keep the surprise on Monday, the state of the promise will change from pending to fulfilled. Once the promise is fulfilled successfully, there will be no other results, which means that the state will not change. On the other hand, if I am too busy and work overtime that day, I forget this thing, and the surprise promised does not come true, the state will change from pending to rejected, and the time cannot be reversed, so the state cannot change again.

A Promise object in the pending state will trigger a fulfilled/rejected state. Once the state changes, the Promise object’s then method will be called. Otherwise, the catch will be triggered. Let’s rewrite the code for 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

Promises also have some drawbacks, such as the inability to cancel promises and the need to catch errors through callbacks.

Promise handwritten, interview sufficient version:

function myPromise(constructor){
    let self=this;
    self.status="pending" // Define the initial state before the state change
    self.value=undefined;// The resolved state is resolved
    self.reason=undefined;// Define the state when the state is rejected
    function resolve(value){
        // The two ==="pending" variables ensure that the state change is irreversible
       if(self.status==="pending"){
          self.value=value;
          self.status="resolved"; }}function reject(reason){
        // The two ==="pending" variables ensure that the state change is irreversible
       if(self.status==="pending"){
          self.reason=reason;
          self.status="rejected"; }}// Catch the construct exception
    try{
       constructor(resolve,reject);
    }catch(e){ reject(e); }}// Define the then method for chained 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

There are other knowledge about Promise, such as the use of promise.all (), promise.race (), etc., which will not be expanded due to space reasons. For more information, see the following article.

Related information:

“Hardcore JS” takes an in-depth look at asynchronous solutions

1. Promises/A+ specifications

60. IteratorWhat is it? What does it do?

Iterator is a prerequisite for understanding article 61. Maybe I don’t have enough IQ 😭, but I have read Iterator and Generator many times and still have a half-grasp of it. Even if I understand it at the time, I forget it later…

Iterator is an interface, or a specification. Provide a unified access mechanism for various data structures. Any data structure can be traversed (that is, all members of the data structure are processed in turn) by deploying the Iterator interface.

The Iterator syntax:

const obj = {
    [Symbol.iterator]:function(){}}Copy the code

[Symbol. Iterator] The name of a property is fixed, and iterators can be used to iterate over objects that have this property.

Iterators are traversed by first getting a pointer to an iterator that initially points to the first piece of data, and then changing the pointer to the next piece of data by calling the next method. Each time next returns an object that has two properties

  • Value represents the data you want to retrieve
  • Done is a Boolean value, where false indicates that the current pointer has a value, and true indicates that the traversal is complete

Iterator has three functions:

  1. Provide a unified and simple access interface for various data structures;
  2. Enable the members of a data structure to be arranged in a certain order;
  3. ES6 creates a new traversal command for… Of loop, Iterator interface for… Of consumption.

Traversal process:

  1. Creates a pointer object pointing to the start of the current data structure. In other words, a traverser object is essentially a pointer object.
  2. The first call to the next method of the pointer object points the pointer to the first member of the data structure.
  3. The second time the next method of the pointer object is called, the pointer points to the second member of the data structure.
  4. Keep calling the next method of the pointer object until it points to the end of the data structure.

Each time the next method is called, information about the current member of the data structure is returned. Specifically, it returns an object with two properties, value and done. The value attribute is the value of the current member, and the done attribute is a Boolean that indicates whether the traversal is complete.

let arr = [{num:1},2.3]
let it = arr[Symbol.iterator]() // Get the iterator in the array
console.log(it.next()) 	// { value: Object { num: 1 }, done: false }
console.log(it.next()) 	// { value: 2, done: false }
console.log(it.next()) 	// { value: 3, done: false }
console.log(it.next()) 	// { value: undefined, done: true }

Copy the code

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

The Generator function can be said to be the concrete implementation of the Iterator interface. The most important feature of a Generator is that it can control the execution of a function.

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 example above is a Generator function. Let’s analyze its execution:

  • First it returns an iterator when the Generator function is called
  • When the first next is performed, the pass is ignored and the function is paused at yield (x + 1), so returns 5 + 1 = 6
  • When next is executed a second time, the argument passed is equal to the value returned by the previous yield. If you do not pass any arguments, yield always returns undefined. Let y be equal to 2 times 12, so the second yield is equal to 2 times 12 over 3, which is 8
  • When next is executed the third time, the passed argument is passed to z, so z = 13, x = 5, y = 24, which adds up to 42

Generator functions are generally not seen much, in fact, it is also a bit around the relationship, and generally with the CO library to use. Of course, we can solve the problem of callback hell with Generator functions.

62. What isasync/awaitHow does it work, what are the pros and cons?

Async /await is a new way to write asynchronous or non-blocking code that builds on promises and is widely regarded as the ultimate and most elegant solution to JS asynchronous operations. It is more readable and concise than promises and callbacks. After all, then() is boring all the time.

Async means asynchronous, and await is short for async wait.

Async is used to declare that a function is asynchronous, while 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 the code

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

Async /await can handle THEN chains better than promises

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}`);
    return takeLongTime(n);
}

Copy the code

These three steps are now implemented with promises 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 doesn’t look much cleaner, elegant, and almost synchronous.

The await keyword can only be used in async functions. Using the await keyword in any non-Async function throws an error. The await keyword waits for the right expression (possibly a Promise) to return before executing the next line of code.Copy the code

The advantages and disadvantages:

The advantage of async/await is that it handles the chain of then calls, makes the code clearer and more accurate, and also solves the callback hell problem gracefully. There are, of course, some disadvantages, as await transforms asynchronous code into synchronous code, which can result in performance degradation if multiple asynchronous codes have no dependencies but use await.

Reference article:

“Hardcore JS” takes an in-depth look at asynchronous solutions

The above 21~25 are the main asynchronous solutions in JavaScript, and they are difficult to figure out and practice.

63. What is the principle of instanceof and how is it implemented

Instanceof can correctly determine the type of the object, because the internal mechanism is to determine whether the prototype of the type can be found in the prototype chain of the object.

Implement instanceof:

  1. First get the prototype of the type
  2. Then get the prototype of the object
  3. Then we keep iterating to see if the stereotype of the object is equal to the stereotype of the type until the stereotype of the object is null, because the stereotype chain ends up being null
function myInstanceof(left, right) {
  let prototype = right.prototype
  left = left.__proto__
  while (true) {
    if (left === null || left === undefined)
      return false
    if (prototype === left)
      return true
    left = left.__proto__
  }
}
Copy the code

64. Js throttling and shaking

Function anti-shaking means that the callback is executed n seconds after the event is fired, and if the event is fired again within n seconds, the time is re-timed. This can be used in some click-request events to avoid sending multiple requests to the back end because of multiple clicks by the user.

Function throttling refers to a specified unit of time, in this unit time, the callback function can only be fired once, if the same unit of time in the same event is fired multiple times, only one can take effect. Throttling can be used on the event listener of the Scroll function to reduce the frequency of event calls.


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

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

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

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

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

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

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

For more information, please refer to:

Easy to understand JS Function throttling and function stabilization

JavaScript Event Throttling and Event Stabilization

JS Tremble and Throttling

65. What are design patterns?

1. The concept

Design patterns are a set of repeatedly used, widely known, catalogued, and catalogued code design experiences. 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 all-win for themselves, others and the system. Design patterns make coding truly engineering. Design patterns are the cornerstone of software engineering, like the blocks of a building.

2. Design principles

  1. S — Single Responsibility Principle

    • A program does one thing well
    • If the function is too complex, break it up and each part remains independent
  2. O — OpenClosed Principle

    • Open for extension, closed for modification
    • When requirements are added, extend new code rather than modify existing code
  3. L — Liskov Substitution Principle

    • A child class can override a parent class
    • Where a parent class can appear, a child class can appear
  4. I – Interface Segregation Principle Interface isolation Principle

    • Keep the interface single and independent
    • Similar to the single responsibility principle, there is more focus on interfaces
  5. D — Dependency Inversion Principle

    • Programming to interfaces that rely on abstractions rather than tools
    • The user cares only about the interface and not about the implementation of the concrete class

3. Types of design patterns

  1. Structural Patterns: Simplify the design of a system by identifying simple relationships between its components.
  2. Creational Patterns: Handle the creation of objects, creating them in the appropriate way for the situation. Conventional methods of object creation can cause design problems or add complexity to the design. The creation pattern solves the problem by controlling the creation of objects in some way.
  3. Behavioral Patterns: Used to identify and implement common Patterns of interactions between objects, thereby increasing the flexibility of these interactions.

66. 9 Common front-end design Patterns

1. Facade Pattern

The look and feel 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 the subsystem easier to use. In short, the design pattern abstracts complex logic from multiple subsystems to provide a more unified, concise, and easy-to-use API. Many commonly used frameworks and libraries follow the design pattern, such as JQuery, which abstracts and encapsulates complex native DOM operations and eliminates interoperability issues between browsers, thus providing a more advanced and easier to use version. In fact, in our daily work, we often use the appearance mode for development, but we do not 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 code

scenario

  • At the beginning of the design, you should consciously separate 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 the development phase, subsystems tend to become more complex as they evolve through refactoring, and adding Facade can provide a simple interface that reduces the dependencies between them.
  • When maintaining a large legacy system, it may be difficult to maintain the system, and it is appropriate to use a Facade. Develop a Facade class for the system to provide a clear interface for the poorly designed and highly complex legacy code, allowing the new system to interact with the Facade objects. The Facade interacts with the legacy code with all the complexity.

advantages

  • Reduce system interdependence.
  • Increase flexibility.
  • Improved security

disadvantages

  • Inconsistent with the open closed principle, if it is difficult to change things, inheritance rewrite is not appropriate.

2. Proxy Pattern

Is to provide a substitute or placeholder for an object in order to control access to it

Suppose that when A receives flowers when he is in A good mood, the probability of Xiao Ming’s confession being successful is

60%, and when A receives flowers when he is in A bad mood, the success rate of Xiao Ming’s confession is infinitely close to 0. Xiao Ming has only known A for two days and can’t tell when A is in A good mood. If you give the flowers to A at an inappropriate time, there is A high possibility that the flowers will be thrown away directly. This bunch of flowers was earned by Xiao Ming after eating instant noodles for 7 days. But A’s friend B knows A very well, so Xiao Ming just gives the flowers to B, B listens to A’s mood change, and then chooses A to hand over the flowers 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 proxy
<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
  • Ruan Yifeng Proxy of ES6

  • 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 pattern 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 can be, in line with the open closed principle;

disadvantages

  • Requests may be processed at different speeds, and indirect access is costly

3. Factory Pattern

The factory pattern defines an interface for creating an object that the subclass determines which class to instantiate. This pattern delays instantiation of a class to subclasses. Subclasses can override interface methods to specify their own object type at creation time.

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.init()
p.fun()
Copy the code

scenario

  • The factory pattern is ideal if you don’t want a subsystem to be strongly coupled to a larger object and you want to choose from many subsystems at runtime

  • Wrap the new operation in a simple way. When you encounter new, you should consider whether to use factory mode.

  • When we need to create different instances depending on the environment, all of which have the same behavior, we 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 complicated, but we only care about the result.

  • The constructor is separated from the creator, conforming to the “open closed principle”

  • When a caller wants to create an object, all he needs to know is its name.

  • Scalability is high. If you want to add a product, you can simply extend a factory class.

disadvantages

  • When adding new products, you need to write new specific product classes, which increases the complexity of the system to some extent

  • Considering the extensibility of the system, it is necessary to introduce an abstraction layer, which is used for definition in the client code, increasing the abstractness and difficulty of understanding the system

When not to

  • When applied to the wrong type of problem, this pattern can introduce a lot 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 would recommend using an explicit constructor to avoid unnecessary overhead.

  • The fact that the object creation process is effectively abstracted behind an interface also creates problems for unit tests that depend on how complex the process can be.

4. Singleton Pattern

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

Implementing the singleton pattern requires addressing the following issues:

  • How do I determine if there is only one instance of Class?

  • How to easily access a unique instance of a Class?

  • How does Class control the instantiation process?

  • How do I limit the number of instances of a Class to one?

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

  • Hide the Class constructor 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 hidden Class constructor
  function FooService() {}

  // Uninitialized singleton object
  let fooService;

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

The key points are:

  1. Use IIFE to create local scopes and execute them instantly.
  2. getInstance() Is a closure that is used to hold and return a singleton object in the 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

  • Divide the namespace to reduce global variables

  • Enhance modularity by organizing your code under a global variable name in a single location for easy maintenance

  • It will only be instantiated once. Simplifies debugging and maintenance of the code

disadvantages

  • Because the singleton pattern provides a single point of access, it can lead to strong coupling between modules
  • This is not good for unit testing. You cannot test a class that calls a method from a singleton in isolation. You can only test it as one with that singleton
  • Unit test together.

5. Strategy Pattern

The simple description of the policy pattern is that 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">
        // The 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 the code
        let registerForm = document.getElementById('registerForm')

        let validataFunc = function() {
          let validator = new Validator()
          validator.add(registerForm.userName, [{
            strategy: 'isNoEmpty'.errorMsg: 'Username cannot be empty'
          }, {
            strategy: 'isNoSpace'.errorMsg: 'Whitespace characters not allowed'
          }, {
            strategy: 'minLength:2'.errorMsg: 'User name must not be less than 2 characters long'
          }])
          validator.add(registerForm.password, [ {
            strategy: 'minLength:6'.errorMsg: 'Password must be at least 6 characters long'
          }])
          validator.add(registerForm.phoneNumber, [{
            strategy: 'isMobile'.errorMsg: 'Please enter the correct mobile 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 differ only by their ‘behavior’, then using the policy pattern allows an object to dynamically choose one behavior among many.

  • A system needs to dynamically choose one of several algorithms.

  • Form validation

advantages

  • Using combination, delegate, polymorphism and other techniques and ideas, multiple conditional selection statements can be effectively avoided

  • It provides perfect support for open-close principles and encapsulates algorithms in independent strategies, making them easy to switch, understand and extend

  • Using composition and delegation to give Context the ability to execute algorithms 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 the differences among them, so as to choose an appropriate strategy

6. Iterator Pattern

If you’re looking at this, the Iterator in ES6 is something you might be familiar with, as mentioned 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 the object.

The iterator pattern solves the following problems:

  • Provides a consistent way to traverse various data structures without knowing the internal structure of the data

  • Provides the ability to traverse a container (collection) without changing the container’s interface

An iterator usually needs to implement the following interface:

  • HasNext () : Determines if the iteration is over. Boolean is returned

  • Next () : Finds and returns the next element

To implement an iterator for an array in Javascript, 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 simpler iterative loop syntax for… “Of” is used only if The object is implemented with The iterable Protocol. Simply put, The object has a method with a Key of Symbol.

Let’s say we implement a Range class for iterating 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 that we are often exposed to. It is also widely used in daily life. For example, if you Subscribe to a blogger’s channel, you will receive push updates when there is content. Another example is the event subscription response mechanism in JavaScript. The idea of the observer pattern is described in one sentence: a subject maintains a set of observers, and when the observed object’s state changes, the observer is notified of the changes by calling one of the observer’s methods.

Subject objects in the Observer pattern generally need to implement the following APIS:

  • Subscribe (): Receives an observer observer object, making it subscribe to itself

  • Unsubscribe (): Receives an observer observer object and causes it to unsubscribe itself

  • Fire (): Fires an event, notifying all observers

Implement observer mode manually in JavaScript:

// The 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 is 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

  • Support for simple broadcast communication, automatically notify all subscribed objects

  • 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 abstractions rather than concrete ones. So that changes on one side don’t affect changes on the other.

disadvantages

  • Overuse can lead to weak connections between objects, which can make programs difficult to track, maintain, and understand

8. Mediator Pattern

In the Mediator model, a Mediator wraps the way a series of objects interact so that they don’t have to interact directly, but instead have the Mediator mediate their interactions so that they can be loosely coupled. When the effects of some objects change, it does not immediately affect the effects of other objects, ensuring that these effects can change independently of each other.

The mediator pattern is similar to the Observer pattern in that both are one-to-many relationships and centralized communication. The difference is that the mediator pattern deals with the interaction between peer objects, while the Observer pattern deals with the interaction between the Observer and the Subject. The intermediary model is a bit like the dating agency. The blind date cannot be directly communicated at the beginning, but is screened through the intermediary to decide who will meet whom.

scenario

  • For example, for shopping cart requirements, there are commodity selection forms, color selection forms, purchase quantity forms and so on, which will trigger the change event. Therefore, these events can be forwarded and processed by intermediaries, so as to realize the decoupling of each event and only maintain the intermediary object.
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

Chat room members:

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

Member.prototype = {
  // Send the 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:

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

Chatroom.prototype = {
  // Add members
  addMember: function (member) {
    this.members[member.name] = member;
    member.chatroom = this;
  },
  // Send the 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);

// Output: Bruce to frank: Hello frank
Copy the code

advantages

  • The objects are loosely coupled and the interaction between them can be changed independently

  • The one-to-many relationship between the mediator and the objects replaces the network many-to-many relationship between the objects

  • If complex coupling between objects makes maintenance difficult, and coupling increases rapidly as the project changes, the broker may need to refactor the code

disadvantages

  • A new intermediary object is added to the system, because the complexity of the interaction between objects shifts to the complexity of the intermediary object, which is often large. The mediator object itself is often a difficult object to maintain.

9. Visitor Pattern

Visitor pattern is a design pattern that separates the algorithm from the object structure. In general terms, 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 implementation of the visitor pattern has the following elements:

  • Visitor Object: Visitor Object, which has a visit() method

  • Receiving Object: Receiving Object. Has an Accept () method

  • Visit (receivingObj) : Used to receive a Receiving Object in a Visitor

  • Accept (Visitor) : For Receving Object to receive a visitor and provide it with the ability to obtain the Receiving Object data by calling the Visitor’s Visit ()

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 classes corresponding to objects in an object structure rarely change, but it is often necessary to define new operations on this object structure

  • There are many different and unrelated operations that need to be performed on objects in an object structure, and you want to avoid “contaminating” the classes of those objects by those operations, and you don’t want to modify those classes when you add new operations.

advantages

  • Consistent with the single responsibility principle

  • Excellent scalability

  • flexibility

disadvantages

  • Specific elements publish details to visitors in violation of the Demeter principle

  • Violates the dependency inversion principle by relying on concrete classes and not on abstractions.

  • Specific elements are more difficult to change

Related references:

JavaScript Design Patterns ES6

The Way to the Front End Interview

JavaScript Design Patterns

Common Design Patterns in JavaScript

💕 Read three things:

  1. Thumb up | you canGo to -- > Favorites -- > ExitAll at once, but don’t forget to like 🤭
  2. Focuses on | point, don’t get lost 😘 next time
  3. You can also go to GitHub and get all of my post source files at 🤗

The latter

The above 66 is the summary of the review of these days, on the whole, in accordance with the order from the shallow to the deep, a small part of the content is not original, related to the reference I have posted links at the end of each, here to especially thank each big guy’s blog, gave me a lot of help ~

Front-end is a hodgehodge, a variety of frameworks emerge in endlessly, but never leave JS, pragmatic foundation is the fundamental, if you think this article is helpful to you, point a like support it ~