Retrospective Treasure Book of Selection of Gold, Nine Silver and Ten JS

Links to the original text: https://juejin.im/post/5d77acb66fb9a06af50ff525

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.

Keywords: Attribute ECMAScript Nginx

Added by Tedglen2 on Thu, 12 Sep 2019 04:55:50 +0300