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:
- If a synchronization task is encountered, directly print Start 111.
- When asynchronous setTimeout is encountered, it is placed in the task queue for execution.
- Then that encounters a Promise is placed in the wait queue.
- Then that encounters a Promise is placed in the wait queue.
- If a synchronization task is encountered, print Start 222.
- 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:
- When you encounter a synchronization code, print “Start 111”.
- When a setTimeout asynchrony is encountered, it is placed in a queue and waits for execution.
- The Promise function is executed, printing “promise111”.
- When setTimeout is encountered, it is asynchronous and put into a queue for execution.
- Then that encounters a Promise waits for a successful return, asynchronously, and is queued.
- In case of synchronization, print start 222.
- 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,
this
Always point to a global object (Window), regardless of strict mode;
this= = =window // true
Copy the code
- Ordinary functions,Nonstrict modeNext to a
window
.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,
- so
call() / 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.from
Convert to a real array. Array.from
You can also take a second argument, which acts like an arraymap
Method 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
- using
new Map
Method, 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 object
target
To store the copied data- use
for in
Iterate 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 defined
target
variable
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
- This is recommended when looping object properties
for... in
“Is used when iterating through groups of numbersfor... of
.for... in
I’m looping over key,for... of
It’s going to loop out value- The array’s key name is a number, but
for... in
Loops are strings with keys “0”, “1”, “2”, and so on.for... in
Not only are numeric key names iterated, but other manually added keys are iterated, even keys on the prototype chain.- In some cases,
for... in
The loop iterates over the key names in any order.for... of
With withfor... in
Same concise syntax, but nofor... in
Those flaws.- Different from the
forEach
Method, it can be withbreak
,continue
andreturn
Use together.for... of
You can’t loop through ordinary objects, you need to pass andObject.keys()
Use in combination, because normal objects do not deploy the Iterator interfacefor... of
Provides 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