Asynchronous loading of JS methods

defer

Internet Explorer 4.0 came along. There will be no Document.write and DOM changes in the defer genus declaration script. The browser will download other scripts with the defer attribute in parallel. It does not block subsequent page processing. Note: All defer scripts must be executed in sequence.

<script type="text/javascript" defer></script>
Copy the code

async

The HTML5 attribute only applies to external scripts, and if in IE both defer and Async exist, defer takes precedence and the script executes when the page is complete. It works the same as defer, but there is no guarantee that the scripts will execute sequentially. They will complete before the onLoad event.

<script type="text/javascript" async></script>
Copy the code

Alternatively, create a script tag and insert it into the DOM with the async property attached

(function(){
    var scriptEle = document.createElement("script");
    scriptEle.type = "text/javasctipt";
    scriptEle.async = true;
    scriptEle.src = "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js";
    var x = document.getElementsByTagName("head") [0]; x.insertBefore(scriptEle, x.firstChild); }) ();Copy the code

However, this loading method prevents the onLoad event from being triggered until the completion of the execution, and many pages now perform additional rendering work during the onload, so it still blocks the initialization of some pages.

Asynchronous loading of onload

Because both methods are done before the onLoad event, they block the onLoad load, and many pages now have code that performs additional rendering while onload is still blocking the initialization of some pages.

(function () {
    if (window.attachEvent) {
        window.attachEvent("load", asyncLoad);
    } else {
        window.addEventListener("load", asyncLoad);
    }
    function asyncLoad() {
        var ga = document.createElement('script');
        ga.async = true;
        ga.src = "http:www.baidu.com";
        var s = document.getElementsByTagName('head') [0];
        s.appendChild(ga);
    }
})();
Copy the code

Accurately determine the JS data type

Object.prototype.toString()

The toString method returns a string representation of an object. By default, it returns a type string.

var o1 = new Object(a); o1.toString()// "[object Object]"

var o2 = {a:1};
o2.toString() // "[object Object]"
Copy the code

The above code shows that calling the toString method on an object returns a string specifying the type of the object.

Object object by itself is not very useful, but by customizing the toString method, you can make an object get the desired string form when the automatic type conversion is performed.

var obj = new Object(a); obj.toString =function () {
  return 'hello';
};

obj + ' ' + 'world' // "hello world"
Copy the code

The above code shows that the toString method is automatically called when an object is used for string addition. Since the toString method is custom, the string hello World is returned.

Arrays, strings, functions, the Date Object is deployed custom toString method respectively, covering the Object. The prototype. The toString method.

[1.2.3].toString() / / "1, 2, 3"

'123'.toString() / / "123"

(function () {
  return 123;
}).toString()
// "function () {
// return 123;
// }"

(new Date()).toString()
// "Tue May 10 2016 09:11:31 GMT+0800 (CST)"
Copy the code

In the above code, arrays, strings, functions, and Date objects that call toString do not return object object because they all override the original toString method.

Use of toString() : Determine the data type

Object. The prototype. The toString method returns the Object type string, and therefore can be used to judge the type of a value.

var obj = {};
obj.toString() // "[object Object]"
Copy the code

The above code calls the toString method of the empty object, which returns a string object object, where the second object represents the constructor for the value. This is a very useful way to determine data types.

Due to the instance objects may be custom toString method, override the Object. The prototype. The toString method, so in order to get type string, use the Object directly. The best prototype. The toString method. The call method of a function can be called on any value, helping us determine the type of the value.

Object.prototype.toString.call(value)
Copy the code

The value of the above code said to call this value Object. The prototype. The toString method.

Different data type of the Object. The prototype. ToString method return values are as follows.

  • Value: return[object Number].
  • String: returns[object String].
  • Boolean: Returns[object Boolean].
  • Undefined: return[object Undefined].
  • Returns null[object Null].
  • Array: returns[object Array].
  • Arguments object: returns[object Arguments].
  • Function: return[object Function].
  • Error object: returns[object Error].
  • Date object: returns[object Date].
  • RegExp object: returns[object RegExp].
  • Other objects: Returns[object Object].

This means that the Object. The prototype. ToString can see what a value type.

Object.prototype.toString.call(2) // "[object Number]"
Object.prototype.toString.call(' ') // "[object String]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(Math) // "[object Math]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call([]) // "[object Array]"
Copy the code

Using this feature, you can write a more accurate type determination function than the Typeof operator.

var type = function (o){
  var s = Object.prototype.toString.call(o);
  return s.match(/\[object (.*?)\]/) [1].toLowerCase();
};

type({}); // "object"
type([]); // "array"
type(5); // "number"
type(null); // "null"
type(); // "undefined"
type(/abcd/); // "regex"
type(new Date()); // "date"
Copy the code

To the type function above, we can add methods that specifically determine certain types of data.

const type = {}
function getType(o){
  var s = Object.prototype.toString.call(o);
  return s.match(/\[object (.*?)\]/) [1].toLowerCase();
};

['Null'.'Undefined'.'Object'.'Array'.'String'.'Number'.'Boolean'.'Function'.'RegExp' ].forEach(function (t) {
  type['is' + t] = function (o) {
    return getType(o) === t.toLowerCase();
  };
});

type.isObject({}) // true
type.isNumber(NaN) // true
type.isRegExp(/abc/) // true
Copy the code

Other methods of determining data types

constructor

Determines whether the constructor is Object

obj.constructor === Object
Copy the code

instanceof

Determines if it is an instance object of a constructor

Note that since arrays are objects, arr instanceof Object is also true.

obj instanceof Object
Copy the code

Therefore, you can determine whether it is an object first, and then whether it is an array

obj instanceof Object&&!Array.isArray(obj)
Copy the code

4. typeof

Judging objects from Typeof is also inaccurate

typeof obj === Object
typeof undefined	       // 'undefined'
typeof null	               // 'object'
typeof true	               // 'boolean'
typeof 123	               // 'number'
typeof "abc"	           // 'string'
typeof function() {}	   // 'function'
typeof {}	               // 'object'
typeof []	               // 'object'
Copy the code

The same thing can be done by elimination

obj&&typeof obj === Object&&!Array.isArray(obj)
Copy the code

Promise then is related to catch

  • Reject, or manually throwing something wrong, must enter the second callback in then, and catch if there is no second callback in then
 var p1=new Promise((resolve,rej) = > {
    //throw new Error(' manually return Error ')
    rej('Failed')
 })

 p1.then(data= >{
    console.log('data::',data);
 },err= > {
    console.log('err::',err)
 }).catch(
    res= > {
    console.log('catch data::', res)
 })
Copy the code

Results:

Err... failedCopy the code
  • If there is no second callback in then, catch is entered
 var p1=new Promise((resolve,rej) = > {
    //throw new Error(' manually return Error ')
    rej('Failed')
 })

 p1.then(data= >{
    console.log('data::',data);
 }).catch(
    res= > {
    console.log('catch data::', res)
 })
Copy the code

Results:

Catch data:: FailedCopy the code
  • A catch can also be entered if there is no then, reject, or manual error thrown
 var p1=new Promise((resolve,rej) = > {
    console.log('there is no resolve')
    //throw new Error(' manually return Error ')
    rej('Failed')
 })

 p1.catch(
    res= > {
    console.log('catch data::', res)
 })
Copy the code

Results:

No resolve catch data:: failedCopy the code
  • Resolve will definitely go into the first callback of THEN, and definitely not into the catch
 var p1=new Promise((resolve,rej) = > {
    resolve('It worked')
 })

 p1.then(data= >{
    console.log('data::',data);
 }).catch(
    res= > {
    console.log('catch data::', res)
 })
Copy the code

Results:

Data: : a successCopy the code
  • Throw New Error is the same as rej, but only one of them will happen
  • In addition, network exceptions (such as disconnection) go directly to catch and not to the second callback of THEN

In general, do not define a Reject callback (the second argument to then) in the THEN () method. Always use the catch method.

// bad
promise
  .then(function(data) {
    // success
  }, function(err) {
    // error
  });

// good
promise
  .then(function(data) { //cb
    // success
  })
  .catch(function(err) {
    // error
  });
Copy the code

In the above code, the second method is better than the first because it catches all the errors in the execution of the previous THEN methods and is closer to the synchronous method (try/catch). Therefore, it is recommended to always use the catch() method instead of the second argument to the then() method.

Event loop

Single threaded model

The single-threaded model refers to JavaScript running on only one thread. That is, JavaScript can only perform one task at a time, and all other tasks must queue up later.

Note that just because JavaScript runs on a single thread does not mean that the JavaScript engine has only one thread. In fact, JavaScript engines have multiple threads, and a single script can only run on one thread (called the main thread), with the other threads cooperating in the background.

The reason JavaScript is single-threaded, rather than multi-threaded, is a matter of history. JavaScript has been single-threaded since its inception because it didn’t want to make the browser too complex, since multiple threads need to share resources and potentially modify each other’s results, which would be too complex for a web scripting language. If JavaScript has two threads at the same time, one thread adds content to the web DOM node, and the other thread removes the node, which thread should the browser use? Should there be a locking mechanism? So, to avoid complexity, JavaScript was originally single-threaded, which has become a core feature of the language and will not change in the future. Advantages and disadvantages of this mode:

  • The advantages are: the implementation is relatively simple, the execution environment is relatively simple;

  • The downside: As long as one task takes a long time, subsequent tasks must wait in line, delaying the execution of the entire program.

A common browser non-response (suspended animation) is usually the result of a single piece of JavaScript code running for so long (such as an infinite loop) that the entire page gets stuck in one place and no other task can be performed. The JavaScript language itself is not slow, but slow in reading and writing external data, such as waiting for Ajax requests to return results. At this point, if the server does not respond, or the network is not smooth, the script will be stalled for a long time.

If the queue is due to a large amount of computation, the CPU is too busy, but many times the CPU is idle because IO operations (input and output) are slow (such as Ajax operations reading data from the network) and have to wait for the results to come out before executing. The designers of the JavaScript language realized that the CPU could simply suspend the pending task and run the next one, regardless of the IO operation. Wait until the IO operation returns the result, then go back and continue the pending task. This mechanism is the “Event Loop” mechanism used in JavaScript.

The single-threaded model, while limiting JavaScript, gives it advantages that other languages don’t. When used well, JavaScript programs don’t clog, which is why Node can handle heavy traffic with very few resources.

In order to make use of the computing power of multi-core CPU, HTML5 proposes the Web Worker standard, which allows JavaScript scripts to create multiple threads, but the child threads are completely controlled by the main thread and cannot operate DOM. So, this new standard doesn’t change the single-threaded nature of JavaScript.

Browser multithreading

The browser is multi-process, each TAB TAB of the browser represents an independent process, among which the browser rendering process (browser kernel) belongs to a multi-process of the browser, mainly responsible for page rendering, script execution, event processing and so on. It includes: GUI rendering thread (responsible for rendering the page, parsing HTML, CSS DOM tree), JS engine thread, event trigger thread, timer trigger thread, HTTP request thread and other main threads.

JS is a single-threaded language, the browser only assigned to JS a main thread, used to perform a task (function), but can only perform one task at a time, the mission to form a task queue waiting to perform, but the front of certain tasks is very time-consuming, such as network request, timers and event listeners, if let them and other tasks, If you queue up to be executed honestly, the execution efficiency will be very low, and even lead to fake death of the page.

Browsers create additional threads for these time-consuming tasks, including HTTP request threads, browser timing triggers, and browser event triggers. These tasks are asynchronous.

Synchronous and asynchronous

All tasks in the program can be categorized into two categories: synchronous and asynchronous.

  • Synchronous tasks: Those tasks that are not suspended by the engine and are queued for execution on the main thread. You can perform the next task only after the previous task is completed.

  • Asynchronous tasks: Tasks that are put aside by the engine and not entered into the main thread but into the task queue. An asynchronous task (in the form of a callback function) is executed on the main thread only if the engine considers it ready to execute (such as an Ajax operation that results from the server). The code after the asynchronous task runs immediately without waiting for the asynchronous task to finish, which means that the asynchronous task has no “blocking” effect.

For example, Ajax operations can be handled as synchronous or asynchronous tasks, at the developer’s discretion. In the case of a synchronous task, the main thread waits for the Ajax operation to return the result and then executes. In the case of asynchronous tasks, the main thread makes an Ajax request and then executes the corresponding callback function after the Ajax operation results.

Task queues and event loops

In JavaScript runtime, in addition to a running main thread, the engine also provides a task queue of various asynchronous tasks that need to be handled by the current program. (In fact, there are multiple task queues depending on the type of asynchronous task. For ease of understanding, assume that there is only one queue.

First, the main thread performs all synchronization tasks. When all the synchronous tasks are completed, the asynchronous tasks in the task queue are viewed. If the condition is met, the asynchronous task is re-entered into the main thread to begin execution, and it becomes a synchronous task. The next asynchronous task enters the main thread to start execution. Once the task queue is empty, the program ends execution.

Asynchronous tasks are usually written as callback functions. Once the asynchronous task re-enters the main thread, the corresponding callback function is executed. If an asynchronous task does not have a callback function, it will not enter the task queue, that is, it will not re-enter the main thread, because there is no callback function to specify the next operation.

Main thread: that is, the main thread constantly reads events from the execution stack, and executes all synchronous code in the stack.

Task Queue: When an asynchronous event is encountered, instead of waiting for the asynchronous event to return the result, the event is suspended in a Queue separate from the execution stack, called a Task Queue.

How does a JavaScript engine know if an asynchronous task has a result, if it can get into the main thread? The answer is that the engine is constantly checking (observer mode), over and over again, to see if pending asynchronous tasks are ready to enter the main thread as soon as the synchronous task is finished. This Loop checking mechanism is called an Event Loop. Wikipedia defines it as: An event loop is a programming construct that waits for and dispatches events or messages in a program. There is also the concept of an execution stack.

Execution stack: When a function is executed, the user clicks the mouse once, Ajax is completed, an image is loaded, and other events occur, as long as the callback function is specified, these events will enter the execution stack queue, waiting for the main thread to read, following the first-in, first-out principle. To be clear, the main thread is a different concept from the execution stack. The main thread specifies which event in the execution stack is now executed.

The execution stack, the main thread and the task queue can be simply understood as follows: the task queue is different students, the main thread is the judge, the execution stack is the runway, and the main thread determines which task in the task queue is executed on the execution stack

As follows:

let a = () = > {
  setTimeout(() = > {
    console.log('Task queue function 1')},0)
  for (let i = 0; i < 5000; i++) {
    console.log('For loop of A')}console.log('A event completed')}let b = () = > {
  setTimeout(() = > {
    console.log('Task queue function 2')},0)
  for (let i = 0; i < 5000; i++) {
    console.log('For loop of B')}console.log('B Event completed')}let c = () = > {
  setTimeout(() = > {
    console.log('Task queue function 3')},0)
  for (let i = 0; i < 5000; i++) {
    console.log('For loop of C')}console.log('C event completed')
}
a();
b();
c();
// setTimeout will be executed after a, b, and c have all been executed
Copy the code

Macro and micro tasks

Asynchronous tasks are divided into macrotasks and microtasks. Tasks registered by different APIS will enter their corresponding queues one by one, and then wait for the Event Loop to push them into the execution stack for execution.

Macrotask: the code currently executing in the call stack is called a macrotask. (Main code fast, timer, etc.).

Script, setTimeout, setInterval, UI rendering, I/O, postMessage, MessageChannel, setImmediate(Node.js environment)

Microtask: a task that needs to be executed before the next macro task starts after the current macro task (the script as a whole is also a macro task). This can be interpreted as a callback event. (Promise. then, proness.nextTick, etc.).

Promise.then, MutaionObserver, process.nexttick (node.js environment)

Events in macro tasks are placed in the callback queue, which triggers thread maintenance; Microtask events are placed in the microtask queue and maintained by the JS engine thread.

Note: Perform the micro task first, then the macro task.

Summary event loop:

  • All synchronization tasks are executed on the main thread, forming an execution stack.
  • In addition to the main thread, there is a “task queue”. When an asynchronous task is encountered, suspend the task first, and place an event in the “task queue” whenever the asynchronous task has a result.
  • Once all synchronization tasks in the Execution stack are completed, the system reads the Task queue. For those asynchronous tasks, the micro tasks enter the execution stack and start executing, and then the macro tasks enter the execution stack and start executing.

Having looked at the mechanism of the event loop, let’s go back to timers

Timer operation mechanism

The operation mechanism of setTimeout and setInterval is to move the specified code out of the current event cycle and wait until the next event cycle to check whether the specified time is reached. If so, execute the corresponding code; If not, keep waiting.

This means that the callbacks specified by setTimeout and setInterval will not start executing until all synchronization tasks of the current event loop have been completed. Since it is uncertain how long the previous tasks will take to complete, there is no guarantee that the tasks specified by setTimeout and setInterval will be executed at the scheduled time.

setTimeout(someTask, 100);
veryLongTask();
Copy the code

The setTimeout of the above code specifies that a task will run after 100 milliseconds. VeryLongTask (someTask) takes 100 milliseconds to complete, so the task will wait until the veryLongTask is finished.

Let’s do another example of setInterval.

setInterval(function () {
  console.log(2);
}, 1000);

sleep(3000);

function sleep(ms) {
  var start = Date.now();
  while ((Date.now() - start) < ms) {
  }
}
Copy the code

In the code above, setInterval requires a 2 to be printed every 1000 milliseconds. However, if the next sleep statement takes 3000 milliseconds to complete, the setInterval must be delayed until that time. Note that setInterval does not have a cumulative effect, that is, instead of printing three twos at once, it only prints one.

Therefore, in fact, the timer is not accurate, it can be said that the execution time must be greater than the set time.

Interview question Practice

Example 1:

console.log('to 111');
setTimeout(function() {
  console.log('setTimeout111');
});
Promise.resolve().then(function() {
  console.log('promise111');
}).then(function() {
  console.log('promise222');
});
console.log('to 222');
Copy the code

Let’s follow the steps to analyze:

  1. If a synchronization task is encountered, directly print Start 111.
  2. When asynchronous setTimeout is encountered, it is placed in the task queue for execution.
  3. Then that encounters a Promise is placed in the wait queue.
  4. Then that encounters a Promise is placed in the wait queue.
  5. If a synchronization task is encountered, print Start 222.
  6. After synchronous execution, the code in the task queue is returned and executed from top to bottom. Then, the macro task setTimeout and the Promise of the micro task are found. Then, the micro task is executed first and then the macro task is executed.

Therefore, the printing sequence is: Start 111, Start 222, promise111, promise222, and setTimeout111.

Note: For promises, the internal code is synchronous, only then() is asynchronous, and new Promise() is synchronous

Example 2:

console.log('to 111');
setTimeout(function () {
  console.log('timeout111');
});
new Promise(resolve= > {
  console.log('promise111');
  resolve();
  setTimeout(() = > console.log('timeout222'));
}).then(function () {
  console.log('promise222')})console.log('to 222');
Copy the code

Analyze:

  1. When you encounter a synchronization code, print “Start 111”.
  2. When a setTimeout asynchrony is encountered, it is placed in a queue and waits for execution.
  3. The Promise function is executed, printing “promise111”.
  4. When setTimeout is encountered, it is asynchronous and put into a queue for execution.
  5. Then that encounters a Promise waits for a successful return, asynchronously, and is queued.
  6. In case of synchronization, print start 222.
  7. When it’s done, it returns and executes the code in the asynchronous queue in order. There is a micro task, then later, so print “promise222” and then perform two macro tasks “timeout111” and “timeout222”.

Therefore, the printing sequence is: Start 111, promise111, Start 222, promise222, timeout111, and timeout222.

RequestAnimationFrame,

Unlike setTimeout and setInterval, requestAnimationFrame does not require an interval. What’s the good of that? Why is requestAnimationFrame called a artifact? This article will detail the new HTML5 timer requestAnimationFrame

The introduction of

Timers have always been at the heart of javascript animation. The key to writing an animation loop is knowing how long the delay is appropriate. On the one hand, the loop intervals must be short enough for the different animations to be smooth and smooth. On the other hand, the loop interval should be long enough to ensure that the browser has the ability to render the resulting changes

Most computer monitors refresh at 60Hz, which equates to about 60 redraws per second. Most browsers limit redrawing to the frequency at which the display can be redrawn, because beyond that the user experience will not improve. Therefore, the optimal loop interval for the smoothest animation is 1000ms/60, which is approximately 16.6ms

The problem with setTimeout and setInterval is that they’re not exact. Their inherent mechanism dictates that the interval parameter really just specifies the time at which the animation code is added to the browser UI thread queue for execution. If other tasks have already been added to the queue, the animation code will wait for the previous task to complete

RequestAnimationFrame uses the system time interval to maintain the best drawing efficiency, and does not cause excessive drawing and overhead because the interval is too short. Also not because the interval is too long, the use of animation is not smooth, so that all kinds of web animation effects can have a unified refresh mechanism, so as to save system resources, improve system performance, improve visual effects

The characteristics of

[1] requestAnimationFrame consolidates all DOM operations in each frame in a single redraw or reflow, and the redraw or reflow interval closely follows the browser refresh rate

[2] requestAnimationFrame will not be redrawn or reflow in hidden or invisible elements, which of course means less CPU, GPU, and memory usage

[3] requestAnimationFrame is an API specially provided by the browser for animation. At runtime, the browser will automatically optimize method calls and animation will automatically pause if the page is not active, effectively saving CPU overhead

use

RequestAnimationFrame is used in much the same way as setTimeout, except that the interval is not required. RequestAnimationFrame takes as an argument a callback function that is invoked before the browser redraws. It returns an integer indicating the timer number, which can be passed to cancelAnimationFrame to cancel execution of the function

requestID = requestAnimationFrame(callback); 
// Console outputs 1 and 0
var timer = requestAnimationFrame(function(){
    console.log(0);
}); 
console.log(timer);/ / 1
Copy the code

The cancelAnimationFrame method is used to cancel timers

// The console doesn't output anything
var timer = requestAnimationFrame(function(){
    console.log(0);
}); 
cancelAnimationFrame(timer);
Copy the code

You can also cancel directly using the return value

var timer = requestAnimationFrame(function(){
    console.log(0);
}); 
cancelAnimationFrame(1);
Copy the code

Compatible with

IE9- Browsers do not support this method and can use setTimeout to make it compatible

[Simple and compatible]

if (!window.requestAnimationFrame) {
    requestAnimationFrame = function(fn) {
        setTimeout(fn, 17);
    };    
}
Copy the code

[Strictly compatible]

if(!window.requestAnimationFrame){
    var lastTime = 0;
    window.requestAnimationFrame = function(callback){
        var currTime = new Date().getTime();
        var timeToCall = Math.max(0.16.7-(currTime - lastTime));
        var id  = window.setTimeout(function(){
            callback(currTime + timeToCall);
        },timeToCall);
        lastTime = currTime + timeToCall;
        returnid; }}Copy the code
if (! window.cancelAnimationFrame) { window.cancelAnimationFrame = function(id) { clearTimeout(id); }; }Copy the code

application

Now use the setInterval, setTimeout, and requestAnimationFrame methods to create a simple implementation effect

【 1 】 setInterval

<div id="myDiv" style="background-color: lightblue; width: 0; height: 20px; line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
var timer;
btn.onclick = function(){
    clearInterval(timer);
    myDiv.style.width = '0';
    timer = setInterval(function(){
        if(parseInt(myDiv.style.width) < 500){
            myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
            myDiv.innerHTML =     parseInt(myDiv.style.width)/5 + The '%';    
        }else{
            clearInterval(timer); }},16);
}
</script>
Copy the code

【 2 】 setTimeout

<div id="myDiv" style="background-color: lightblue; width: 0; height: 20px; line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
var timer;
btn.onclick = function(){
    clearTimeout(timer);
    myDiv.style.width = '0';
    timer = setTimeout(function fn(){
        if(parseInt(myDiv.style.width) < 500){
            myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
            myDiv.innerHTML =     parseInt(myDiv.style.width)/5 + The '%';
            timer = setTimeout(fn,16);
        }else{
            clearTimeout(timer); }},16);
}
</script>
Copy the code

【 3 】 requestAnimationFrame

<div id="myDiv" style="background-color: lightblue; width: 0; height: 20px; line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
var timer;
btn.onclick = function(){
    myDiv.style.width = '0';
    cancelAnimationFrame(timer);
    timer = requestAnimationFrame(function fn(){
        if(parseInt(myDiv.style.width) < 500){
            myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
            myDiv.innerHTML =     parseInt(myDiv.style.width)/5 + The '%';
            timer = requestAnimationFrame(fn);
        }else{ cancelAnimationFrame(timer); }}); }</script>
Copy the code

This points to the problem

preface

The this keyword is a very important syntax point. It’s no exaggeration to say that most development tasks can’t be accomplished without understanding what it means.

Simply put, this is the object where the attribute or method is “currently” located.

this.property
Copy the code

In the code above, this represents the object on which the property property is currently located.

Here is a practical example.

var person = {
  name: 'Joe'.describe: function () {
    return 'Name:'+ this.name; }}; person.describe()// "name: Zhang SAN"
Copy the code

In the code above, this.name represents the object with the name attribute. Since this.name is called within the describe method and the current object of the describe method is Person, this points to Person and this.name is Person.name.

Since an attribute of an object can be assigned to another object, the current object of the attribute is mutable, that is, the reference to this is mutable.

var A = {
  name: 'Joe'.describe: function () {
    return 'Name:'+ this.name; }};var B = {
  name: 'bill'
};

B.describe = A.describe;
B.describe() // "name: Li Si"
Copy the code

In the above code, the a.describe attribute is assigned to B, so b.describe means that the current object of the describe method is B, so this.name refers to B.name.

Whenever a function is assigned to another variable, the reference to this changes.

var A = {
  name: 'Joe'.describe: function () {
    return 'Name:'+ this.name; }};var name = 'bill';
var f = A.describe;
f() // "name: Li Si"
Copy the code

In the above code, a.describe is assigned to the variable f, and the internal this points to the object on which F is running (in this case, the top-level object, which in browsers is window), so name is the global name value.

The essence

The JavaScript language has this design because of the data structure in memory.

var obj = { foo:  5 };
Copy the code

The above code assigns an object to the variable obj. The JavaScript engine generates an object {foo: 5} in memory and assigns the object’s memory address to the variable obj. In other words, the variable obj is a reference. To read obj.foo later, the engine gets the memory address from obj and then reads the original object from that address, returning its foo property.

The original object is stored in a dictionary structure, with each attribute name corresponding to an attribute description object. For example, the foo property in the example above is actually stored in the following form.

{
  foo: {
    [[value]]: 5
    [[writable]]: true
    [[enumerable]]: true
    [[configurable]]: true}}Copy the code

Note that the value of the foo property is stored in the value property of the property description object.

The structure is clear, but the problem is that the value of an attribute can be a function.

var obj = { foo: function () {}};Copy the code

At this point, the engine stores the function separately in memory and assigns the address of the function to the value property of foo.

{
  foo: {[[value]]: address of the function... }}Copy the code

Since a function is a single value, it can be executed in different environments (contexts).

var f = function () {};
var obj = { f: f };

f() // Execute separately

obj.f() // obj environment execution
Copy the code

JavaScript allows you to reference other variables in the current environment inside the function body.

var f = function () {
  console.log(x);
};
Copy the code

In the code above, the variable x is used inside the function body. This variable is provided by the runtime environment.

Now the problem is that since functions can be executed in different runtime environments, there needs to be a mechanism to get the current runtime context from within the function body. So, here comes this, which is designed to refer to the current running environment of the function inside the function body.

var f = function () {
  console.log(this.x);
}
Copy the code

In the above code, this.x in the function body refers to x of the current running environment. This refers to the environment in which it is currently running.

var f = function () {
  console.log(this.x);
}

var x = 1;
var obj = {
  f: f,
  x: 2};// Execute separately
f() / / 1

// obj environment execution
obj.f() / / 2
Copy the code

In the above code, function f executes in the global environment, this.x points to x of the global environment; In the obj environment, this.x points to obj.x.

Use occasions

This is mainly used in the following situations.

Global environment

  • In the browser global environment,thisAlways point to a global object (Window), regardless of strict mode;
this= = =window // true
Copy the code
  • Ordinary functions,Nonstrict modeNext to awindow.Strict modeNext to aundefined.
function f() {
    console.log(this= = =window);
}
f() // true

function f() {
    'use strict';
    console.log(this= = =undefined);
}
f() // true
Copy the code

In the constructor

In the constructor, this refers to the instance object.

var Obj = function (p) {
  this.p = p;
};
Copy the code

The above code defines a constructor, Obj. Since this refers to the instance object, defining this.p inside the constructor is equivalent to defining the instance object to have a p attribute.

var o = new Obj('Hello World! ');
o.p // "Hello World!"
Copy the code

Object

If the object’s method contains this, this points to the object on which the method is run. This method assigns to another object, which changes the reference to this.

However, this rule is not easy to follow. Look at the code below.

var obj ={
  foo: function () {
    console.log(this); }}; obj.foo()// obj
Copy the code

In the code above, when the obj.foo method executes, its internal this points to obj.

However, each of the following uses will change the direction of this.

/ / a
(obj.foo = obj.foo)() // window
/ / 2
(false || obj.foo)() // window
/ / is three
(1, obj.foo)() // window
Copy the code

In the code above, obj.foo is a value. When this value is actually called, the runtime environment is no longer OBj, but global, so this no longer refers to OBj.

Inside the JavaScript engine, obj and obj.foo are stored at two memory addresses, called address one and address two.

When obj.foo() is called this way, it calls address two from address one, so address two runs at address one, and this refers to obj.

However, in each of the above cases, the call is made directly from address two, in which case the runtime environment is the global environment, so this refers to the global environment. The above three cases are equivalent to the following code:

/ / a
(obj.foo = function () {
  console.log(this); }) ()/ / is equivalent to
(function () {
  console.log(this); }) ()/ / 2
(false || function () {
  console.log(this); }) ()/ / is three
(1.function () {
  console.log(this); }) ()Copy the code

If the method is not in the first layer of the object, this refers only to the current layer of the object and does not inherit from the layer above.

var a = {
  p: 'Hello'.b: {
    m: function() {
      console.log(this.p); }}}; a.b.m()// undefined
Copy the code

In the above code, the A.B.M method is at the second level of the A object, and the this inside the method does not point to A, but to A.B, because the code below is actually executed.

var b = {
  m: function() {
   console.log(this.p); }};var a = {
  p: 'Hello'.b: b
};

(a.b).m() // same as b.m()
Copy the code

This in the array method

The map and foreach methods of arrays allow you to supply a function as an argument. This should not be used inside this function.

var o = {
  v: 'hello'.p: [ 'a1'.'a2'].f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    });
  }
}

o.f()
// undefined a1
// undefined a2
Copy the code

In the code above, this in the foreach callback refers to the window object, so it does not fetch o.v. The reason for this is the same as in the previous paragraph: the inner this does not point to the outside, but to the top object.

One way to solve this problem, as mentioned earlier, is to fix this with an intermediate variable.

var o = {
  v: 'hello'.p: [ 'a1'.'a2'].f: function f() {
    var that = this;
    this.p.forEach(function (item) {
      console.log(that.v+' '+item);
    });
  }
}

o.f()
// hello a1
// hello a2
Copy the code

Another approach is to take this as the second argument to the foreach method, fixing its runtime environment.

var o = {
  v: 'hello'.p: [ 'a1'.'a2'].f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    }, this);
  }
}

o.f()
// hello a1
// hello a2
Copy the code

Or use the arrow functions of ES6.

var o = {
  v: 'hello'.p: [ 'a1'.'a2'].f: function f() {
    this.p.forEach((item) = >{
        console.log(this.v + ' ' + item);
    });
  }
}

o.f()
// hello a1
// hello a2
Copy the code

This in the prototype chain

  • The this of the method in the prototype chain still refers to the object that called it
var o = {
  f : function(){ 
    return this.a + this.b; }};var p = Object.create(o);
p.a = 1;
p.b = 4;

console.log(p.f()); / / 5
Copy the code
  • As you can see from the above code, there is no property f in p. When p.f() is executed, it looks for the prototype chain of P, finds f and executes it, but it has nothing to do with the fact that inside the function this points to object P, just remember who calls to whom.

DOM event handlers

  • The this inside the event handler refers to the object that triggered the event
var oBox = document.getElementById('box');
oBox.onclick = function () {
    console.log(this)   //oBox
}
Copy the code

setTimeout & setInterval

For the callback inside the delay function, this points to the global object Window.

// Code by default
function Person() {  
    this.age = 0;  
    setTimeout(function() {
        console.log(this);
    }, 3000);
}

var p = new Person();// Returns the window object after 3 seconds= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =// Bind
function Person() {  
    this.age = 0;  
    setTimeout((function() {
        console.log(this);
    }).bind(this), 3000);
}

var p = new Person();// After 3 seconds return the newly generated object Person{... }

Copy the code

This in the arrow function

Since the arrow function does not bind this, it captures the this value of its context (defined position) as its own this value,

  • socall() / apply() / bind()Methods to arrow functions simply pass in arguments and have no effect on its this.
  • Given that this is lexical, rules related to this are ignored in strict mode. (You can ignore whether it is in strict mode)
function Person() {  
    setInterval(() = > {
        console.log(this)	//Person
    }, 3000);
}

var p = new Person();

Copy the code

This points to Person

let a = () = > {
  console.log(this)
}
a()

Copy the code

The above code this points to the window

The dynamic toggling of this, while creating great flexibility for JavaScript, also makes programming difficult and ambiguous. Sometimes, you need to stick to this to avoid unexpected situations. JavaScript provides call, apply, and bind methods to switch/fix the reference to this. Here’s how to bind this.

Function.prototype.call()

use

The call method of a function instance can specify the point of this inside the function (that is, the scope in which the function is executed), and then call the function within the specified scope.

var obj = {};

var f = function () {
  return this;
};

f() === window // true
f.call(obj) === obj // true
Copy the code

In the code above, when the global environment runs f, this points to the global environment (the browser is the window object); The call method can change the reference of this by specifying that this points to object obj, and then run the function f in the scope of object obj.

The argument to the call method should be an object. If the arguments are null, null, and undefined, the global object is passed in by default.

var n = 123;
var obj = { n: 456 };

function a() {
  console.log(this.n);
}

a.call() / / 123
a.call(null) / / 123
a.call(undefined) / / 123
a.call(window) / / 123
a.call(obj) / / 456
Copy the code

In the code above, the this keyword in function A returns 123 if it refers to a global object. If you point this keyword to an obj object using the call method, the result is 456. As you can see, if the call method has no arguments, or if the argument is null or undefined, it points to a global object.

Note: In strict mode or VUE (vUE defaults to strict mode), passing null points to NULL and passing window points to Window

If the call method argument is a raw value, the raw value is automatically converted to the corresponding wrapper object and passed to the Call method.

var f = function () {
  return this;
};

f.call(5)
// Number {[[PrimitiveValue]]: 5}
Copy the code

In the code above, call takes 5, is not an object, and is automatically converted to a wrapper object (an instance of Number), bound to this inside f.

parameter

The call method can also accept multiple arguments.

func.call(thisValue, arg1, arg2, ...)
Copy the code

The first argument to call is the object to which this refers, followed by arguments to which the function is called.

function add(a, b) {
  return a + b;
}

add.call(this.1.2) / / 3
Copy the code

In the code above, the call method specifies that the this inside the add function is bound to the current environment (object) with arguments 1 and 2, so the add function runs with 3.

One application of the call method is the native method of calling an object.

var obj = {};
obj.hasOwnProperty('toString') // false

// Override the inherited hasOwnProperty method
obj.hasOwnProperty = function () {
  return true;
};
obj.hasOwnProperty('toString') // true

Object.prototype.hasOwnProperty.call(obj, 'toString') // false
Copy the code

Obj does not have a toString property (hasOwnProperty); obj does have a toString property (hasOwnProperty). If this method is overridden (this overrides not the method on the Object prototype, but calls its own method first when OBJ creates a method that is identical to the prototype), it will not get the correct result. The call method solves this problem by placing the original definition of the hasOwnProperty method on an OBJ object so that it does not affect the result whether or not there is a method with the same name on obJ.

Manual implementation

Implementation idea:

  • Change this to point to: You can use the target function as an attribute of this object
  • Use array objects of the arguments class to implement variable length arguments
  • You cannot add attributes to the object, so delete is required at the end

Here is the manual implementation:

/ * * *@desc Description *@param Object [object] Specifies the object to be pointed to */
Function.prototype.myCall = function (object) {
    let obj = object || window;	// If this is not passed, this points to the window
    var fn = Symbol(a);// Symbol attribute to determine the uniqueness of fn
    obj[fn] = this;				// This refers to the function that called myCall, and assigns a reference to function A to obj's fn property. Now, when function A is called it's going to point to obj
    let arg = [...arguments].slice(1);	// Get the second and all subsequent arguments (arg is an array)
    letresult = obj[fn](... arg);// a reference call to obj, the object passed in
    delete obj[fn] // The obj attribute cannot be added, so it must be removed
    return result If function A has a return value, there is a return value. If function A has no return value, there is no return value
}
Copy the code

The above code:

  • When myCall does not pass a parameter, do compatibility, pointing to window.
  • The myCall function in which this refers to the caller, the function that executes myCall, is called function A.
  • Assigning a reference to function A to obj.fn is the same as assigning a reference to function A to obj inside this. This is where the this binding is implemented.
  • Insert arguments into slice. Of course, the arguments to function A start from the second position, hence slice(1).
  • If obj. Fn returns a value, result returns a value, and if obj. Fn returns a value, result returns a value, and vice versa.

The following is the result:

function a(c) {
    console.log('aaa'.this.a + this.b + c);
}
const obj = {
    a: 1.b: 2
}
a.myCall(obj, 1) / / print 4
Copy the code

MyCall changes the direction in a to obj, x equals obj is an instance of A, that is, obj calls a, a.myCall(obj, 1) is equivalent to obj. A (1) so print 1+2+1=4.

Function.prototype.apply()

The apply method is similar to the call method in that it changes this and then calls the function. The only difference is that it takes an array as an argument to the function, in the following format.

func.apply(thisValue, [arg1, arg2, ...] )Copy the code

The first argument to the apply method is also the object to which this points. If set to null or undefined, this specifies the global object. The second argument is an array whose members are, in turn, passed as arguments to the original function. The arguments of the original function must be added individually in the call method, but in the apply method, they must be added as arrays.

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

f.call(null.1.1) / / 2
f.apply(null[1.1]) / / 2
Copy the code

In the code above, the f function, which originally took two arguments, now accepts an array with the apply method.

You can do some interesting things with this.

Changes an empty element of the array toundefined

The apply method uses the Array constructor to change an empty element of the Array to undefined.

Array.apply(null['a'.'b']) // [ 'a', undefined, 'b' ]
Copy the code

The difference between an empty element and undefined is that the array forEach method skips empty elements, but not undefined. Therefore, when iterating through the inner elements, you get different results.

Of course, ES6 can be implemented using extension operators.

const arr = ['a'.'b'];
[...arr] // 'a',undefined,'b'
Copy the code

Convert an array-like object

Additionally, an array-like object (such as the Arguments object) can be turned into a real array using the slice method of array objects.

Array.prototype.slice.apply({0: 1.length: 1}) / / [1]
Array.prototype.slice.apply({0: 1}) / / []
Array.prototype.slice.apply({0: 1.length: 2}) // [1, undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]
Copy the code

The apply method takes objects, but returns an array as a result, which serves the purpose of converting objects to arrays. As you can see from the code above, this method works only if the object being processed has a length attribute and the corresponding numeric key.

Manual implementation

The difference between call and applay is the passing parameter

/ * * *@desc Description *@param Object [object] Specifies the object to be pointed to */
Function.prototype.myCall = function (object) {
    let obj = object || window;	// If this is not passed, this points to the window
    var fn = Symbol(a);// Symbol attribute to determine the uniqueness of fn
    obj[fn] = this;				// This refers to the function that called myCall and refers to function A
    let arg = [...arguments].slice(1);	// Get the second and all subsequent arguments (arg is an array)
    let result = obj[fn](arg); // a reference call to obj, the object passed in, where an array is passed directly
    delete obj[fn] // The obj attribute cannot be added, so it must be removed
    return result If function A has a return value, there is a return value. If function A has no return value, there is no return value
}
Copy the code

The difference with call is that the following code passes an array

let result = obj.fn(arg);
Copy the code

Function.prototype.bind()

The bind() method is used to bind this in the function body to an object and return a new function.

var d = new Date(a); d.getTime()/ / 1481869925657

var print = d.getTime;
print() // Uncaught TypeError: this is not a Date object.
Copy the code

In the code above, we assign the d.goettime () method to the variable print, and then call print() to return an error. This is because this inside getTime() is bound to an instance of the Date object, and when assigned to the variable print, this inside no longer refers to the instance of the Date object.

The bind() method solves this problem.

var print = d.getTime.bind(d);
print() / / 1481869925657
Copy the code

In the code above, the bind() method binds this inside the getTime() method to the D object, where it is safe to assign the method to other variables.

The argument to the bind method is the object to which this is bound. Here is a clearer example.

var counter = {
  count: 0.inc: function () {
    this.count++; }};var func = counter.inc.bind(counter);
func();
counter.count / / 1
Copy the code

In the code above, the counter.inc() method is assigned to the variable func. You have to bind this inside of Inc () to counter, otherwise you’ll get an error.

Bind () can also take more arguments, binding them to the arguments of the original function.

var add = function (x, y) {
  return x * this.m + y * this.n;
}

var obj = {
  m: 2.n: 2
};

var newAdd = add.bind(obj, 5); // Pass 5 to x
newAdd(5) // 20 passes 5 to y
Copy the code

In the code above, the bind() method, in addition to binding this, also binds the first argument x to 5 of add() and returns a new function, newAdd(), which takes an additional argument y to run.

Note: If the first argument to bind() is null or undefined, this is bound to the global object, and this points to the top-level object (window) when the function is run.

The bind() method has a few usage caveats.

Return a new function each time

The bind() method returns a new function each time it is run, which can cause some problems. For example, when listening for events, the following cannot be written.

element.addEventListener('click', o.m.bind(o));
Copy the code

In the code above, the click event binds an anonymous function generated by the bind() method. This would make it impossible to unbind, so the following code is invalid.

element.removeEventListener('click', o.m.bind(o));
Copy the code

The correct way to write this is as follows:

var listener = o.m.bind(o);
element.addEventListener('click', listener);
/ /...
element.removeEventListener('click', listener);
Copy the code

Used in conjunction with callback functions

Callbacks are one of the most common patterns in JavaScript, but a common mistake is to treat methods containing this as callbacks directly. The solution is to bind counter. Inc () to counter using the bind() method.

var counter = {
  count: 0.inc: function () {
    'use strict';
    this.count++; }};function callIt(callback) {
  callback();
}

callIt(counter.inc.bind(counter));
counter.count / / 1
Copy the code

In the code above, the callIt() method calls the callback function. If you pass counter. Inc directly, this inside counter. Inc () will point to the global object when called. Once you bind counter. Inc to counter using the bind() method, you don’t have this problem; this always points to counter.

There is also a more subtle case where some array methods can take a function as an argument. The this pointer inside these functions is also likely to be wrong.

var obj = {
  name: 'Joe'.times: [1.2.3].print: function () {
    this.times.forEach(function (n) {
      console.log(this.name); }); }}; obj.print()// There is no output
Copy the code

In the code above, the this of this. Times inside obj. Print refers to obj. However, the this.name inside the forEach() callback refers to the global object, so there is no way to get the value

To solve this problem, bind this with the bind() method.

obj.print = function () {
  this.times.forEach(function (n) {
    console.log(this.name);
  }.bind(this));
};

obj.print()
/ / zhang SAN
/ / zhang SAN
/ / zhang SAN
Copy the code

In combination withcall()Methods using

Using the bind() method, you can override the use of some JavaScript native methods, such as the slice() method of arrays.

[1.2.3].slice(0.1) / / [1]
/ / is equivalent to
Array.prototype.slice.call([1.2.3].0.1) / / [1]
Copy the code

In the above code, the slice method splits an array from [1, 2, 3] at the specified start and end positions. The essence of this is to call the array.prototype.slice () method on [1, 2, 3], so you can express this with the call method and get the same result.

The call() method essentially calls the function.prototype.call () method, so the above expression can be overwritten with bind().

var slice = Function.prototype.call.bind(Array.prototype.slice);
slice([1.2.3].0.1) / / [1]
Copy the code

The code above means:

The Call method is a method on the Function prototype, and slice is a method. Therefore, bind changes the direction of call to slice, which is equivalent to an instance of Function. Call is a method under the slice instance, which turns slice into the object of the call method. When the call to perform is to slice actually call the call method Array. The prototype. Slice. The call.

Similar notation can be used for other array methods.

var push = Function.prototype.call.bind(Array.prototype.push);
var pop = Function.prototype.call.bind(Array.prototype.pop);

var a = [1 ,2 ,3];
push(a, 4)
a // [1, 2, 3, 4]

pop(a)
a / / [1, 2, 3]
Copy the code

If you go a step further and bind the function.prototype. call method to the function.prototype. bind object, that means that the call form of bind can also be overridden.

function f() {
  console.log(this.v);
}

var o = { v: 123 };
var bind = Function.prototype.call.bind(Function.prototype.bind);
bind(f, o)() / / 123
Copy the code

The code above means:

  • Change call to point to bind. That is, bind becomes an instance of call, the equivalent of bind calling call. Therefore, return to the Function. The prototype. The bind. Call the Function.

  • Bind (f, o) means: call the Function. The prototype. The bind. Call Function, change the first parameter is the bind, pointing to the f. The second argument is an argument to bind. So f calls bind, and it returns an f that points to O.

  • Bind (f,o)() with parentheses is equivalent to executing the f function to o. The last printed result, this, points to O, so 123 is printed.

Manual implementation

Unlike Call and applay, bind returns a function

/ * * *@desc Description *@param Object [object] Specifies the object to be pointed to */
Function.prototype.myBind = function (object) {
    let obj = object || window;	// If this is not passed, this points to the window
    var fn = Symbol(a);// Symbol attribute to determine the uniqueness of fn
    obj[fn] = this;				// This refers to the function that called myCall and refers to function A
    let arg = [...arguments].slice(1);	// Get the second and all subsequent arguments (arg is an array)
    /* Returns a function that internally executes the called a function, passing in the argument */
    const fBind = function () {
        /* If this is an instance of fBound, then fBind is new, otherwise obj */
        if (this instanceof fBind) {
            object[fn].applay(this, arg)
        } else{ object[fn](... arg) }delete object[fn]
    }
    return fBind
}
function a(c) {
    console.log('aaa'.this.a + this.b + c);
}
const obj = {
    a: 1.b: 2
}
a.myBind(obj, 1) ();/ / print 4
Copy the code

algorithm

Implement the Fibonacci sequence

  • Mathematically it is defined recursively, starting at 0, that is F(0)=0, F(1)=1. The value of the third digit is equal to adding the first two digits. So F of 2 is equal to F of 0 plus F of 1 is 0 plus 1, and so on.
F(0) = 0;
F(1) = 1;
F(n) = F(n - 1) + F(n - 2);
Copy the code
  • Formula version: recursion
function fib(n) {
    if (n < 0) throw new Error('The number entered cannot be less than 0');
    if (n < 2) {
        return n;
    }
    return fib(n - 1) + fib(n - 2);
}
Copy the code

The normal recursive version is simple and straightforward logic, but the problem with this version is that there is a lot of double-counting. For example, if n is 5, fib(4) + fib(3), if n is 4, fib(3) + fib(2), then fib(3) is double-counting. Run fib(50) and wait for half a day.

  • Remove recursive versions of repeated computations
    function fib(n) {
        if (n < 0) throw new Error('The number entered cannot be less than 0');
        if (n < 2) return n;
        function _fib(n, a, b) {
            if (n === 0) return a;
            return _fib(n - 1, b, a + b);
        }
        return _fib(n, 0.1);
    }
Copy the code

Making the first two digits as parameters cleverly avoids double calculations and significantly improves performance. N is decremented, the first two digits are incremented (Fibonacci sequence increments), and the code subtracts and increments.

  • Array methods
function fib(n) {
    if (n < 0) throw new Error('The number entered cannot be less than 0');
    if (n < 2) {
        return n;
    }
    let list = [];
    list[0] = 0;
    list[1] = 1;
    for (let i = 1; i < n; i++) {
        list[i + 1] = list[i] + list[i - 1];
    }
    return list[n];
}
Copy the code

Array method, define an array, the zeroth and the first value is 0,1, the third value is equal to the first two values added, into the array in turn.

  • Based on ES6 Generator
function* fib(n) {
        if (n < 0) throw new Error('The number entered cannot be less than 0');
        let [f0, f1] = [1.1]
        let count = 0;
        if (n === 0) {
            yield 0
        } else {
            while (count < n) {
                yieldf0; [f0, f1] = [f1, f0 + f1]; count++; }}}// 0 1 1 2 3
    console.log([...fib(0)]);
    console.log([...fib(1)]);
    console.log([...fib(2)]);
    console.log([...fib(3)]);
    console.log([...fib(4)]);
Copy the code

Sort an array

Bubble sort

  • Compare any digit in the array with the next digit. If you want to sort from smallest to largest, put the smallest digit in front and the largest digit in back. Simply swap them.

    function sortA(arr) {
        for (var i = 0; i < arr.length; i++) {
            for (var j = i; j < arr.length; j++) {
                if (arr[i] > arr[j]) {
                    // Because we need to swap values, we will replace the latter value, we need to save first
                    var index = arr[j];
                    / / exchange valuearr[j] = arr[i]; arr[i] = index; }}}return arr;
    }
    Copy the code

Quick sort

  • You take a value from the middle of the array, and you compare that value to each of the values in the array, if you put aside the values that are greater than that, put aside the values that are less than that, and then you combine them, and then you compare them, and so on.

    function sortB(arr) {
      // If there is only one digit, there is no need to compare
      if (arr.length <= 1) {
        return arr;
      }
      // Get the index of the intermediate value
      var len = Math.ceil(arr.length / 2);
      // Cut the median value
      var cur = arr.splice(len, 1);
      // Less than the middle value is put in here
      var left = [];
      // Put the larger ones inside
      var right = [];
      for (var i = 0; i < arr.length; i++) {
        // Check whether the value is greater than
        if (cur > arr[i]) {
          left.push(arr[i]);
        } else{ right.push(arr[i]); }}// Through recursion, the previous round of good array merge is performed, and the comparison is performed again.
      return sortB(left).concat(cur, sortB(right));
    }
    Copy the code

Array to heavy

  • There are many ways to remove the duplicate of an array. Here are three:

Es5 cycle

Define a variable that temporarily holds a new array. If it exists in the new array variable, it will not operate. If it does not exist, it will be pushed into the new array variable.

var arr = ['1'.'2'.1.'1'.'4'.'9'.'1'];
function newArr(arr) {
    const tempArr = [];
    for (var i = 0; i < arr.length; i++) {
        if(tempArr.includes(arr[i])) { tempArr.push(arr[i]); }}return tempArr;
}
console.log(newArr(arr));
Copy the code

Es6 Set method

var arr = [1.1.2.9.6.9.6.3.1.4.5];
function newArr(arr) {
  return Array.from(new Set(arr))
}
console.log(newArr(arr))
Copy the code
  • Array.from()The method is to convert an array-like object or traversable object into a real array.
let arrayLike = {
    0: 'tom'.1: '65'.2: 'male'.'length': 4
}
let arr = Array.from(arrayLike)
console.log(arr) / / [' Tom ', '65' and 'male']
Copy the code
  • To convert an array-like object into a true array, the following conditions must be met:
  • An array object of this class must have a length attribute, which specifies the length of the array. If there is no length attribute, the transformed array is an empty array.
  • The property name of this array object must be a numeric or string number
  • Ps: The property names of this array object can be quoted or unquoted
  • ES6 provides a new data structure, Set. It is similar to an array, but the values of the members are unique and there are no duplicate values.new Set()Returns the Set object, therefore, can be usedArray.fromConvert to a real array.
  • Array.fromYou can also take a second argument, which acts like an arraymapMethod to process each element and place the value in the returned array. As follows:
let arr = [12.45.97.9797.564.134.45642]
let set = new Set(arr)
console.log(Array.from(set, item= > item + 1)) // [13, 46, 98, 9798, 565, 135, 45643]
Copy the code
  • Convert a string to an array:
let  str = 'hello world! ';
console.log(Array.from(str)) // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!"]
Copy the code

Es6’s Map() method

  • usingnew MapMethod, where set takes the value of the array as the key and 1 as the value. And then GET the key, if it’s already set and the key is 1, it just pushes it into AA.
var arr = [1.1.2.9.6.9.6.3.1.4.5];
function newArr(arr) {
  const tempArr = [];
  const temp = new Map(a);for (let i = 0; i < arr.length; i++) {
    if(! temp.has(arr[i])) { temp.set(arr[i],1); tempArr.push(arr[i]); }}return tempArr;
}
console.log(newArr(arr))
Copy the code
  • A Set is like an array, and a Map is like a Key, Value pair. ES6 provides Map data structures. It is a collection of key-value pairs similar to objects, but the range of “keys” is not limited to strings. Values of all types (including objects) can be used as keys.

A multidimensional array becomes a one-dimensional array

Flat method

Array.prototype.flat() is used to “flatten” nested arrays into one-dimensional arrays. This method returns a new array with no effect on the original data.

Flat () “flattens” only one layer by default. If you want to “flatten” a nested array of multiple layers, you can write the argument to the flat() method as an integer representing the number of layers you want to flatten, which defaults to 1.

[1.2[3.4]].flat(2)
Copy the code

If no matter how many layers are nested, you want to convert to a one-dimensional array, you can use the Infinity keyword as an argument.

[1.2[3.4]].flat(Infinity)
Copy the code

The toString method

When an array is called toString, it becomes a single layer of strings no matter how many times the array is called. This can then be converted to an array by calling split. The downside is that other data types are also converted to strings

const arr = [1."2".'aaaa'[3[4]]]
function flatten(arr) {
    return arr.toString().split(', ')}Copy the code

The reduce method

The return value of reduce is: concat is used to connect the result of the last operation, prev, and the subsequent value. The latter value is mainly used to determine whether it is an array. If it is an array, the function is called again; otherwise, the next value is returned

const arr = [1."2".'aaaa'[3[4]]]
function flatten(arr) {
    return arr.reduce(function (prev, next) {
        return prev.concat(Array.isArray(next) ? flatten(next) : next)
    }, [])
}
Copy the code

Design patterns

Singletons, factories, publish subscriptions

  • Singleton pattern: In its core structure values contain a special class called a singleton. A class has only one instance, that is, a class has only one object instance.

  • Factory pattern: Objects are created without exposing creation logic to clients, and by using a common interface to point to newly created objects.

  • Publish subscribe pattern: In software architecture, publish subscribe is a messaging paradigm in which a sender of a message (called a publisher) does not directly send a message to a specific recipient (called a subscriber). Instead, they divide published messages into different categories without knowing which subscribers, if any, might exist. Similarly, subscribers can express interest in one or more categories and receive only messages of interest without knowing which publishers (if any) exist.

other

The depth of the clone

The following are deep-copy objects

export const deepCopy = (source) = > {
	if (typeofsource ! = ='object') return source; // If it is not an object or array
	let target = Array.isArray(source) ? [] : {} // Array compatibility
	for (var k in source) {
		// If it is an object or array, execute this function again
        target[k] = typeof source[k] === 'object'? deepCopy(source[k]): source[k]
	}
	return target
}
Copy the code
  • First determine if it is not an object or array and return the original data
  • Defines an empty array or objecttargetTo store the copied data
  • usefor inIterate over the raw data by filtering out the attributes on the prototype.
  • If it is an object or array, execute the function again.
  • Otherwise, the data is copied and put into the definedtargetvariable

Encapsulate native Ajax as a promise

var  myNewAjax=function({url,data=null} = {}){
    return new Promise(function(resolve,reject){
        let xhr = new XMLHttpRequest();
        xhr.open('get',url);
        xhr.send(data);
        xhr.onreadystatechange=function(){
            if(readyState==4) {if(xhr.status==200) {var json=JSON.parse(xhr.responseText);
                    resolve(json)
                }else{
                    reject('error'); }}})}Copy the code

Js listens for object properties to change

Let’s say I have a user object here,

(1) In ES5, object.defineProperty can be used to monitor existing attributes

Object.defineProperty(user,'name', {set:function(key,value){}})Copy the code

Disadvantages: If the ID is not in the User object, you cannot listen for changes in the ID

(2) In ES6, it can be implemented through Proxy

var  user = new Proxy({}, {set:function(target,key,value,receiver){}})Copy the code

If a user attribute does not exist in the user, the user. Id definition can also be used to listen for changes in the attribute

The difference between for in and for of

  1. This is recommended when looping object propertiesfor... in“Is used when iterating through groups of numbersfor... of.
  2. for... inI’m looping over key,for... ofIt’s going to loop out value
  3. The array’s key name is a number, butfor... inLoops are strings with keys “0”, “1”, “2”, and so on.
  4. for... inNot only are numeric key names iterated, but other manually added keys are iterated, even keys on the prototype chain.
  5. In some cases,for... inThe loop iterates over the key names in any order.
  6. for... ofWith withfor... inSame concise syntax, but nofor... inThose flaws.
  7. Different from theforEachMethod, it can be withbreak,continueandreturnUse together.
  8. for... ofYou can’t loop through ordinary objects, you need to pass andObject.keys()Use in combination, because normal objects do not deploy the Iterator interface
  9. for... ofProvides a unified operation interface for traversing all data structures.

Ordinary objects implement Iterator

Use for… The of loop, which can write methods over any object. Native JavaScript objects have no traversal interface and cannot use for… The of loop is ready to be used by adding this interface to it via a Generator function.

function* objectEntries(obj) {
  let propKeys = Reflect.ownKeys(obj);

  for (let propKey of propKeys) {
    yield[propKey, obj[propKey]]; }}let jane = { first: 'Jane'.last: 'Doe' };

for (let [key, value] of objectEntries(jane)) {
  console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe
Copy the code

In this code, the object Jane does not have an Iterator interface. Of traversal. To add the traverser interface to the Generator function objectEntries, use the for… Of traverses. Another way to add an iterator interface is to add a Generator function to the Symbol. Iterator property of the object.

function* objectEntries() {
  let propKeys = Object.keys(this);

  for (let propKey of propKeys) {
    yield [propKey, this[propKey]]; }}let jane = { first: 'Jane'.last: 'Doe' };

jane[Symbol.iterator] = objectEntries;

for (let [key, value] of jane) {
  console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe
Copy the code

Implement bind

Function.prototype.bind = function (obj) {
    var arg = Array.prototype.slice.call(arguments.1); // Get the parameters except obj and convert them to arrays
    var context = this; // This refers to the direct caller, which is aa below, the function that called bind
    return function () {
        arg = arg.concat(Array.prototype.slice.call(arguments));
        context.apply(obj, arg); // aa calls apply to change this to refer to obj}}function aa(a, b, c, d) {
    console.log('this', a, b, c, d, this); 
}
aa.bind(Object.1.2) (3.4) // Prints 1, 2, 3, 4 Object objects
Copy the code