Preface
In this golden ninety silver ten days, we present a JS selected review pamphlet for you, I hope you will laugh!
this for normal functions and arrow functions
function fn() { console.log(this); // 1. {a: 100} var arr = [1, 2, 3]; (function() { console.log(this); // 2. Window })(); // Ordinary JS arr.map(function(item) { console.log(this); // 3. Window return item + 1; }); // Arrow function let brr = arr.map(item => { console.log("es6", this); // 4. {a: 100} return item + 1; }); } fn.call({ a: 100 }); //Copy code
The trick is simple. There are three basic cases: the normal function of es5, the arrow function of es6, and the new function returned by changing the context through bind.
(1) es5 general function:
The function is called directly, and the context must be that the window function is called as an object property, such as obj.foo(). The context is that the object itself obj is called by new, and this is bound to the returned instance.
(2) es6 arrow function:
It does not have this in itself, and looks up the scope until window. See the following code:
function run() { const inner = () => { return () => { console.log(this.a); }; }; inner()(); } run.bind({ a: 1 })(); // Output: 1 //Copy code
(3) The new function returned by the bind binding context is the context bound by the first bind, and the bind is invalid for the "arrow function". See the following code:
function run() { console.log(this.a); } run.bind({ a: 1 })(); // output: 1 // Multiple binds, the context is determined by the context of the first bind run.bind({ a: 2 }).bind({ a: 1 })(); // output: 2 //Copy code
Finally, let's talk about the priority of these methods: New > bind > object invocation > direct invocation
Raw Data Type and Judgment Method
Topic: The original data type in JS?
Seven primitive types are defined in ECMAScript:
Boolean
String
Number
Null
Undefined
Symbol(es6)
Note: The original type does not contain Object and Function
Topic: Common judgment methods?
There are typeof and instanceof in judging. For array judgment, use Array.isArray():
-
typeof:
-
typeof basically can correctly judge the data type.
-
Both typeof null and typeof [1, 2, 3] return "object"
-
ES6 Added: typeof Symbol() Returns "symbol"
-
instanceof:
Specialized for instance and constructor correspondence
function Obj(value) { this.value = value; } let obj = new Obj("test"); console.log(obj instanceof Obj); // output: true //Copy code
Determine whether it is an array: [1, 2, 3] instance of Array
Array.isArray(): ES6 is added to determine whether it is an'Array'. Array.isArray({}) returns false.
Event stream
Event Bubble and Event Capture
Event streams are divided into bubbling and capturing, and the sequence is capturing first and then bubbling.
Event Bubble: The trigger event of the child element is passed to the parent until the root node stops. In this process, relevant events can be captured at each node. Bubbles can be terminated by stopPropagation.
Event capture: In contrast to "event bubbles", execution starts at the root node and passes on to the child until the target node.
addEventListener gives a third parameter that supports both bubbling and capture: false by default, event bubbling; true by default, event capture.
<div id="app" style="width: 100vw; background: red;"> <span id="btn">Point me</span> </div> <script> // Event capture: output "outer click event trigger" first, and then output "inner click event trigger" var useCapture = true; var btn = document.getElementById("btn"); btn.addEventListener( "click", function() { console.log("Inner layer click Event triggering"); }, useCapture ); var app = document.getElementById("app"); app.onclick = function() { console.log("Outer layer click Event triggering"); }; </script> //Copy code
DOM0 and DOM2 DOM2: The addEventListener, which defines the DOM event stream, captures + bubbles.
Class DOM0:
Binding on events directly in html tags and on series events in JS Note: Now generic DOM2 level events have the following advantages:
Event stream bubbling + capture can be supported by binding/unloading events: equivalent to the same event per node, with at least two processing opportunities for the same kind of event, multiple functions can be bound
ES5 inheritance
Method 1: Binding constructors
Disadvantage: Cannot inherit parent prototype methods/attributes
function Animal() { this.species = "Animal"; } function Cat() { // Execute the construction method of the parent class with the context as the instance object Animal.apply(this, arguments); } /** * Test code */ var cat = new Cat(); console.log(cat.species); // output:Animal //Copy code
Method 2: Prototype chain inheritance
Disadvantages: It is impossible to pass parameters to the parent constructor; methods defined on the prototype chain of subclasses have sequential problems. Note: Exchanging prototype chains in js requires fixing the prototype.constructor pointing problem.
function Animal(species) { this.species = species; } Animal.prototype.func = function() { console.log("Animal"); }; function Cat() {} /** * func The method is invalid because the back prototype chain is redirected to the Animal instance */ Cat.prototype.func = function() { console.log("Cat"); }; Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; // Fix: Repoint Cat.prototype.constructor to itself /** * Test code */ var cat = new Cat(); cat.func(); // output: Animal console.log(cat.species); // undefined //Copy code
Method 3: Combination inheritance
Combining the binding constructor with the prototype chain inheritance, the disadvantage is that the constructor of the parent class is called twice.
function Animal(species) { this.species = species; } Animal.prototype.func = function() { console.log("Animal"); }; function Cat() { Animal.apply(this, arguments); } Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; /** * Test code */ var cat = new Cat("cat"); cat.func(); // output: Animal console.log(cat.species); // output: cat //Copy code
Method 4: Parasitic combinatorial inheritance improves the disadvantage of combinatorial inheritance. It only needs to call the constructor of the parent class once. This is currently the most recommended way to inherit.
/** * Core Code of Parasitic Composite Inheritance * @param {Function} sub Subclass * @param {Function} parent Parent class */ function inheritPrototype(sub, parent) { // Get the prototype of the parent class var prototype = Object.create(parent.prototype); // Change the constructor pointing prototype.constructor = sub; // Parent Prototype Assignment to Subclass sub.prototype = prototype; } function Animal(species) { this.species = species; } Animal.prototype.func = function() { console.log("Animal"); }; function Cat() { Animal.apply(this, arguments); // Only one constructor is called } inheritPrototype(Cat, Animal); /** * Test code */ var cat = new Cat("cat"); cat.func(); // output: Animal console.log(cat.species); // output: cat //Copy code
Prototype and prototype chain
- All reference types (arrays, objects, functions) have a _proto_ attribute
- All functions have a prototype attribute, and the attribute value is a common object.
- All reference types (arrays, objects, functions), _proto_ attribute values point to the prototype attribute values of its constructor
- Note: The arrow function of ES6 has no prototype attribute, but has _proto_ attribute.
How to understand the prototype in JS?
Prototypes are used to inherit attributes and methods.
When trying to get an attribute of an object, if the object itself does not have this attribute, it will be found in its _proto_ (that is, the prototype of its constructor).
How to understand the prototype chain in JS?
_ Proto_ is an attribute of every object, because prototype is also an object, so it also has the _proto_ attribute, and _proto_ links each object in series to form a chain, which is called prototype chain.
Scope and Scope Chain
How to understand the scope and scope chain of JS?
(1) Scope
ES5 has "global scope" and "function scope". The let s and const s of ES6 enable JS to use "block-level scopes".
(2) Scope chain
The definition of the current scope has not been found, continue to search for the parent scope until the global scope. This hierarchical relationship is the scope chain.
event loop
-
Perform a macro task (not retrieved from the event queue in the stack)
-
If a micro-task is encountered during execution, it is added to the task queue of the micro-task.
-
Once the macro task has been executed, all the micro tasks in the current micro task queue are executed immediately (sequentially)
-
When the current macro task is finished, check the rendering, and then the GUI thread takes over the rendering.
-
After rendering, the JS thread continues to take over and starts the next macro task (retrieved from the event queue)
JS is single-threaded, and all tasks above it are executed in two places: execution stack and task queue. The former is to store synchronous tasks; the latter is to put an event in an asynchronous task when it has a result. When all tasks on the execution stack are finished (empty stack), JS reads the task queue and drops the executable tasks from the task queue to the execution stack. This process is circular, so it's called EventLoop.
Execution context
JS execution context is divided into global execution context and function execution context.
Global execution context
When parsing JS, create a global execution context environment. Take out the variables and function declarations that are about to be executed in the code (internal functions are not counted, because you don't know when functions are executed). Unassigned variables are undefined.
The following code output: undefined; rather than throwing Error. Because when parsing JS, variable a is stored in the global execution context.
console.log(a); var a = 1; Copy code
(2) Function execution context
It's similar to the global execution context, but with this and arguments and parameters.
In JS, this is the key word, which acts as a built-in variable whose value is determined at the time of execution (not at the time of definition).
closure
Definition: The variable object of an external function should have been destroyed after it was called, but the existence of closures still allows us to access the variable object of an external function, which is an important concept of closures.
How to solve memory leak?
The solution is to explicitly expose an interface for cleaning variables:
function mockData() { const mem = {}; return { clear: () => (mem = null), // Explicit Exposure Cleaning Interface get: page => { if (page in mem) { return mem[page]; } mem[page] = Math.random(); } }; } //Copy code
Implementing the Millimeter Separator
One sentence version:
(123456789).toLocaleString('en-US');//"123,456,789" Copy code
Handwritten call
Point: If a function is an attribute of an object, then this is the object by calling the function through the object's. operator
let obj = { a: "a", b: "b", test: function(arg1, arg2) { console.log(arg1, arg2); // this.a is a; this.b is b. console.log(this.a, this.b); } }; obj.test(1, 2); //Copy code
Knowing the key to implementation, here's our simulated call:
Function.prototype.call2 = function(context) { if (typeof this !== "function") { throw new TypeError("Error"); } // The default context is window context = context || window; // Save the default fn const { fn } = context; // The key to the previous discussion is to automatically bind this by calling the function itself as an attribute of the object context. context.fn = this; const args = [...arguments].slice(1); const result = context.fn(...args); // Restore default fn context.fn = fn; return result; }; // Here is the test code function test(arg1, arg2) { console.log(arg1, arg2); console.log(this.a, this.b); } test.call2( { a: "a", b: "b" }, 1, 2 ); //Copy code
Implementation of apply
apply and call implementations are similar, except that the incoming parameter form is an array rather than a comma-separated sequence of parameters.
Therefore, with the help of the... Operator provided by es6, it is very convenient to realize the transformation of array and parameter sequence.
Function.prototype.apply2 = function(context) { if (typeof this !== "function") { throw new TypeError("Error"); } context = context || window; const { fn } = context; context.fn = this; let result; if (Array.isArray(arguments[1])) { // Converting an array to a comma-separated sequence of parameters through the... Operator result = context.fn(...arguments[1]); } else { result = context.fn(); } context.fn = fn; return result; }; /** * Here is the test code */ function test(arg1, arg2) { console.log(arg1, arg2); console.log(this.a, this.b); } test.apply2( { a: "a", b: "b" }, [1, 2] ); //Copy code
Handwritten deep copy
function deepClone(src, target) { const keys = Reflect.ownKeys(src); let value = null; for (let key of keys) { value = src[key]; if (Array.isArray(value)) { target[key] = cloneArr(value, []); } else if (typeof value === "object") { // If it's an object and not an array, then recursively invoke a deep copy target[key] = deepClone(value, {}); } else { target[key] = value; } } return target; } //Copy code
Handwritten Event Emitter
Implementing ideas: This involves the relevant knowledge of "Subscription/Publishing Mode". Refer to the specific effects of addEventListener and removeEventListener to achieve.
// Array empty: // arr = []; arr.length = 0; arr.splice(0, arr.length) class Event { constructor() { this._cache = {}; } // Register events: If no such type exists, create the associated array on(type, callback) { this._cache[type] = this._cache[type] || []; let fns = this._cache[type]; if (fns.indexOf(callback) === -1) { fns.push(callback); } return this; } // Triggered events: Triggered for all event functions in a type trigger(type, ...data) { let fns = this._cache[type]; if (Array.isArray(fns)) { fns.forEach(fn => { fn(...data); }); } return this; } // Delete events: Delete the array corresponding to the event type off(type, callback) { let fns = this._cache[type]; // Check for event binding for type if (Array.isArray(fns)) { if (callback) { // Unload the specified callback function let index = fns.indexOf(callback); if (index !== -1) { fns.splice(index, 1); } } else { // Empty all fns = []; } } return this; } } // Here are the test functions const event = new Event(); event .on("test", a => { console.log(a); }) .trigger("test", "hello"); //Copy code
Promise
- Three states: pending, fulfilled, rejected
- Promise instances are executed once they are created
- The Promise process is divided into two branches: pending => resolved and pending => rejected.
- After the Promise status changes, the code will still execute after:
const warnDemo = ctx => { const promise = new Promise(resolve => { resolve(ctx); console.log("After resolved, but Run"); // This statement will still be executed }); return promise; }; warnDemo("ctx").then(ctx => console.log(`This is ${ctx}`)); //Copy code
Promise property
The state changes only once
Once the state of Promise changes, it will remain in that state forever, and it will not change again.
False bubble
The error of the Promise object is "bubbling" and passes back until it is caught. That is, errors are always caught by the next catch statement
"Eating Errors" Mechanism
Promise eats internal errors without affecting the operation of external code. So catch is needed to prevent the error message from being lost.
async/await
The async function returns a Promise object, and you can use the then method to add a callback function.
When a function is executed, it returns to await, waits for the asynchronous operation to complete, and then executes the following statements in the body of the function.
This is also its most popular feature: it allows asynchronous code to write like synchronous code, and facilitates the control of sequence.
It can be used to implement a sleep function to block processes:
function sleep(millisecond) { return new Promise(resolve => { setTimeout(() => resolve, millisecond); }); } /** * Here is the test code */ async function test() { console.log("start"); await sleep(1000); // Sleep for 1 second console.log("end"); } test(); // Executing test functions //Copy code
Although convenient, it can not replace Promise, especially we can easily use Promise.all() to achieve concurrency, while async/await can only achieve serial (bad will cause performance problems).
Comparisons between EsModule and Common JS
At present, there are four modules management specifications in the js community: AMD, CMD, Common js and EsModule. ES Module is a modular scheme implemented natively, which differs from Common js in the following aspects:
CommonJS supports dynamic import, EsModule currently does not support synchronous import, because for the server side, the files are local, synchronous import even if the main thread is stuck has little impact. EsModule is an asynchronous import, because for browsers, you need to download files. If you also use synchronous import, it will have a great impact on rendering. CommonJs output is a shallow copy of the value. The reference of esModule output value will be compiled into require/exports to execute.
Browser Rendering Process
Generating DOM Tree Based on HTML Code
Generating CSSDOM from CSS
Integrating DOM tree and CSSOM into RenderTree
Start rendering and displaying based on RenderTree
Blocking rendering when encountering script tags
Why do script tags block rendering?
Common threads in browsers are rendering threads, JS engine threads, HTTP threads and so on.
For example, when we open an Ajax request, we start an HTTP thread.
Similarly, we can only use threads to explain why direct DOM operations are slower and more performance-intensive. Because JS engine threads and rendering threads are mutually exclusive. Direct operation of DOM involves communication between two threads that are mutually exclusive, so it costs more.
After all, JS can modify DOM, and if the UI works when the JS is executed, it may lead to unsafe (unexpected) rendering results.
What is the sequence of onload and DOMContentLoaded triggers?
DOMContentLoaded is done before onload.
The DOMContentLoaded event is triggered after the DOM tree is built, and we can use js to access elements at this stage.
The scripts for async and defer may not have been executed yet.
Pictures and other resource files may still be downloaded.
The load event is triggered after all the resources on the page have been loaded. Usually we don't use this event because we don't need to wait that long.
document.addEventListener("DOMContentLoaded", () => { console.log("DOMContentLoaded"); }); window.addEventListener("load", () => { console.log("load"); }); //Copy code
Cross domain
- JSONP: Implemented through script tags, but only GET requests
- Reverse proxy: proxy of nginx
- CORS: Backend allows cross-domain resource sharing
Implementation of JSONP
// Define callback function const Response = data => { console.log(data); }; // Construct < script > tags let script = document.createElement("script"); script.src = "http://xxx.com?callback=Response"; // Add < script > label to document and send GET request document.body.appendChild(script); //Copy code
epilogue
May you all find a satisfactory job in this season of gold, silver and ten!
Author: _Dreams
Link: https://juejin.im/post/5d77acb66fb9a06af50ff525
Source: Nuggets
Copyright belongs to the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.