Ali push two-dimensional code, need to take
Data types in JS
Basic type: Number string Boolean NULL undefined symbol bigint
Reference data type: the object (including the Date, the RegExp, Function, Array, Math..)
The function of symbol
Symbol is one of the basic types. Symbols is a basic type that cannot be reconstructed. In this case, symbols is similar to the situation where instances created by objects are not equal to each other, but at the same time symbols is a basic type of data that cannot be changed.
const s1 = Symbol(); const s2 = Symbol(); console.log(s1 === s2); //falseCopy the code
As you can see, the values created by symbol are not the same, even if the same parameters are passed in. Note that symbol is not instantiated. So symbol can be used
- As the property name of the object, the property name is guaranteed not to be duplicated. But note that the symbol cannot pass for… in… Through out
const s1 = Symbol('a');
const s2 = Symbol('a');
var obj = {};
obj['a'] = "aaa"
obj[s1] = "asjdkl"
obj[s2] = "u can not see me"
for (const key in obj) {
console.log(key);
}
//a
Copy the code
If you want to get, can through the Object. GetOwnPropertySymbols () to obtain
for (const key of Object.getOwnPropertySymbols(obj)) {
console.log(key);
}
//Symbol(a)
//Symbol(a)
Copy the code
Type conversion
It’s basically just looking at the code to get results, but to fully understand the principles behind it, I recommend yuba’s blog github.com/mqyqingfeng…
How to determine the type of a variable
Typeof is detectable for primitive types (except Null), but reference types return object uniformly
Instance of is used to check whether the stereotype of a constructor appears on the stereotype chain of an instance function
Best method is to use the Object. The prototype. The toString method, it can detect any Type of return is the result of the [Object Type], in the form of basic can achieve all types of testing, we use the following code to demonstrate.
// Implement a public interface for detecting types
function detectType(type) {
return function(obj) {
return {}.toString.call(obj) === `[object ${type}] `}}// Expand to suit your needs, remembering to capitalize the first letter of the type
const isArray = detectType("Array")
const isFunc = detectType("Function")
const isRegExp = detectType("RegExp")
console.log(isArray([1.2.3])); //true
console.log(isArray("[1, 2, 3]")); //false
console.log(isFunc(detectType)); //true
console.log(isRegExp(/test/)); //true
Copy the code
Five,This point
The orientation of this in JS can be roughly divided into the following four scenarios
- Used in object methods,this refers to the current object
var obj = {
a: "hhh".test() {
return this.a; }}console.log(obj.test());//'hhh'
Copy the code
- Used in separate functions
- In strict mode, this refers to undefined
- In non-strict mode, this refers to a global object, such as Windo
var a = "jjj"
var obj = {
a: "hhh".test() {
return this.a; }}const test = obj.test;
console.log(test());//"jjj"
Copy the code
- Specified by call apply bind
Each of these can change this by passing in the value of this to be changed. The difference is that call\apply executes the function when it changes, while bind does not execute it and returns the function
The first parameter of call\apply is the value of this to be changed. The difference is that call passes in a list of parameters and apply passes in an array of parameters
- The constructor
If a function is a constructor, this refers to the object it instantiates
- Arrow function
Arrow functions do not create their own this, they only inherit this from the upper level of their scope chain, and arrow functions cannot use call\apply\bind to change the direction of this
What is a closure?
A closure is a function that can access variables in other scopes
Cause of occurrence
The first step is to understand the concept of scope chains. The scope of a function is where it is created, that is, a function has its scope defined when it is created.
When a function encounters a variable during execution, it will first check whether the variable exists in its scope. If it does not, it will search up from the parent scope until it finds the location. Otherwise, undefined is reported
function f1() {
var a = 1;
function f2() {
console.log(a);
}
f2();
}
f1()/ / 1
Copy the code
So the essence of a closure is that there are references to the parent scope. Note that the code above is not a closure, and we are not asking for variables that are not in its scope by calling f1(), because a is in F1, we are just calling F2 from F1. Let’s change the above example to closure form, and we’ll call F2 outside
function f1() { var a = 1; var f2 = function() { console.log(a); } return f2 } const clousure = f1(); clousure(); / / 1Copy the code
We call f2 externally as the return value, and you can see that we have access to the f1 variable, which is the closure,
It also shows that f2’s scope is where it was created, not where it was called.
Explain the prototype chain
Each function has a prototype property, and when it is used as a constructor, its instantiated function has a _proto_ property, which implements the constructor’s prototype property
The prototype function accesses the properties and methods of its parent through prototype and iterates through the chain until the prototype of Object is at the top of the chain.
When accessing a property or method, it looks for the current Object first. If it does not have one, it follows the prototype chain until it finds the very top of the Object’s prototype (null).
In addition, each stereotype has a constructor property that points to the associated constructor
DOM event flow and event delegate
The DOM event flow is divided into three phases:
- Capture phase
- The target stage
- Bubbling phase
- Capture phase: In an event bubbling model, the capture phase does not respond to any events;
- Target phase: The target phase refers to the event response to the lowest element that triggers the event;
- Bubbling stage: In the bubbling stage, the triggering response of an event extends from the lowest target layer to the outermost layer (root node). The event proxy binds the event that needs to be responded by the inner layer to the outermost layer by using the mechanism of event bubbling
Event flow describes the order in which events are received from a page. IE and Netscape have two opposite concepts, IE with bubbling flow from bottom to top, and Netscape with event capture flow from top to bottom.
The first two arguments are the name of the click event and the callback to execute. The third argument is whether capture is enabled and the phase in which the event occurred. The default is false, which is the bubbling stream.
Event delegation, in general, delegates the time of an element or a group of elements to its parent element or to its outer element. When the event responds to the element to which it is bound, the event bubble mechanism triggers the binding event of its outer element, and then executes the function on the outer element. In some scenarios, performance can be optimized. For example, to add click events to all lists, if we use bubble flow, we need to add click events to each element, whereas with event delegate, we only need to bind one event to ul.
How to implement JS inheritance
- Inheritance through the prototype chain
function Parent() {
this.lastName = "wang"
}
Parent.prototype.asset = ['house'.'car']
function Child() {
}
Child.prototype = new Parent();
var child = new Child();
var child1 = new Child();
child.asset.push("plane")
console.log(child.lastName);//"wang"
console.log(child1.asset);//[ 'house', 'car', 'plane' ]
Copy the code
Advantages: Access to properties and methods of the parent class and properties and methods on the prototype. Disadvantages: Inherit methods that are reference types and are modified by one of the subclasses, then all are affected
- Inheritance via call
function Parent() {
this.lastName = "wang";
this.hobby = ['a'.'b']
}
Parent.prototype.asset = ['house'.'car']
function Child() {
Parent.call(this)}var child = new Child();
var child1 = new Child();
child.hobby.push("c")
console.log(child.lastName);/ / "wang"
console.log(child1.hobby);//['a', 'b']
console.log(child1.asset);//undefined
Copy the code
Advantages: Each subclass is guaranteed to maintain its own properties. Disadvantages: No access to properties and methods on the stereotype chain
- Combination of inheritance
Combine the first two
function Parent() {
this.lastName = "wang";
this.hobby = ['a'.'b']
}
Parent.prototype.asset = ['house'.'car']
function Child() {
Parent.call(this)
}
Child.prototype = new Parent();
var child = new Child();
var child1 = new Child();
child.hobby.push("c")
console.log(child.lastName);
console.log(child1.hobby);
//wang
//[ 'a', 'b' ]
//[ 'house', 'car' ]
Copy the code
Disadvantages: The parent class is executed every time an instance of a subclass is created (for internal implementation of new, see the next topic).
- Optimized combinatorial inheritance
Modify the prototype assignment statement as follows
Child.prototype = Parent.prototype;
Copy the code
Printing child reveals another problem
How can the constructor of child be Parent, so we need to manually modify it
Child.prototype = Parent.prototype;
Child.prototype.constructor = Child;
Copy the code
You can also use the object.create () method to create an Object with a specified prototype
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Copy the code
What does the new operator do
- Create an empty object
- Points the _proto_ attribute of an empty object to the prototype of the constructor
- Point this to this object
- Return this object
Code implementation:
function newFactory(constructor) {
var obj = {};
obj.__proto__ = constructor.prototype;
constructor.apply(obj, [].slice.call(arguments.1));
return obj
}
function Test(name) {
this.name = name;
}
var t = newFactory(Test, "tom")
console.log(t.name);//"tom"
Copy the code
Let const
Both let and const are es6 syntax. Var has no block-level scope, so it is easy to pollute global variables. Let and const have block-level scope. Let is a block-scoped var const that specifies a constant and cannot be changed once defined.
Variables declared by let and cons must not have been used before, or an error will be reported. That is, variables declared by let and const can only be referenced after the declaration.
Another point to note about const is that when it is defined as a reference type, it is not allowed to add content to it.
After verification, we can continue to add content to it. But you can’t replace it, because the original value, the variable directly refers to that value, and you can’t assign it as long as it’s not equal, whereas the reference type, the variable just has a reference to its memory address, and as long as the address of that reference hasn’t changed, you can still operate on it.
Async promise,async/await
- Introduction of promise (status…)
- Use of async await
There are asynchronous programming methods
- The callback function
- promise
- Async/await, etc
Callback functions can cause callback hell in complex scenarios, affecting code readability and execution rate.
Promise is an es6 asynchronous programming scheme with three states: Pending,fullfilled, and Rejected. Once the state changes, it cannot be reversed. There are two kinds of corresponding changes:
Pending ——> Fullfilled (resolved)
Pending ——> Rejected (Rejected rejected)
The logic behind asynchronous calls is implemented through the then method, and chained calls are also supported.
Async /await is the syntax of ES7, which is also used to realize asynchronous programming. The syntax is to add async before function keyword, which means async function, and await can only be used in async function.
Async converts any function to a promise, which is one of the characteristics of asynchronous functions. Await can be used before any function that returns a Promise function and will pause there until the Promise returns the result.
Async/Awaite basically achieves asynchronous logic with synchronous code style, making code more concise.
13. Event Loop
- The principle of
- Look at the code and tell the result
Js is a single-threaded language, so all tasks can only be queued to be done one by one, which is obviously inefficient. So event loop is proposed to solve this problem.
In the main program, there are two threads, one that runs the program itself, called the main thread, and the other that communicates with other threads (mainly I/O operations), called the Event loop thread
When an I/O operation occurs, the main thread tells the Event Loop thread to inform the corresponding I/O module to execute it. The main thread then executes the following code. When the I/O is over, the Event Loop thread passes the result to the main thread, which then executes the corresponding callback. The whole mission is over.
Macro task micro task
To keep the tasks running smoothly on the main thread, V8 stores and executes the tasks in queues. There are two types of task queues, including the above task queue and a delay queue. It specializes in timer callbacks such as setTimeout/setInterval. The tasks in both types of task queues are macro tasks
Microtasks are usually scheduled for things that should happen after the current script finishes executing, such as reacting to a series of actions, or making something asynchronous without the cost of a macro task
There are two schemes for the execution of micro-tasks: one is to execute the micro-tasks successively after all the macro tasks are completed; the other is to check the micro-task queue after the execution of a macro task, and execute the micro-tasks successively if they are not empty, and then execute the macro task.
The latter is obviously more satisfying than the latter, otherwise the application will get stuck if the callback is not executed.
Common macro tasks are: setTimeout setTimeInterval Common micro tasks are: “MutationObserver, Promise.then(or.reject), and other technologies developed based on Promise (such as the FETCH API), including V8’s garbage collection process.”
nextTick
Process. nextTick is a task queue independent of eventLoop.
The queue is checked at the end of each eventLoop phase, and if there are tasks in it, those tasks take precedence over microtasks.
What are the macro tasks and micro tasks
Figure source link
Exercise 1:
console.log('start');
setTimeout(() = > {
console.log('timeout');
});
Promise.resolve().then(() = > {
console.log('resolve');
});
console.log('end');
//start
//end
//resolve
//timeout
Copy the code
- First, the entire script is executed as a macro task and is executed directly when it encounters synchronized code
- Print the start
- Put setTimeout into the macro task queue
- Put promise.resolve into the microtask queue
- Print the end
- Perform all microtasks and print resolve
- Execute the macro task and print timeout
Ex 2
setTimeout( () = > console.log(4))
new Promise(resolve= > {
resolve()
console.log(1)
}).then(_= > {
console.log(3)})console.log(2)
/ / 1 2 3 4
Copy the code
That is, the code executed during the instantiation of a New Promise is synchronized, while the callbacks registered in then are executed asynchronously.
The asynchronous task is checked for completion and the corresponding callback is executed after the synchronous code is completed, while the microtask is executed before the macro task
Anti – shake and throttling
Stabilization: After an event is called, it is timed from the beginning no matter how many times it is called before execution
Throttling: No matter how many times an event is invoked, it is always executed at a specified time interval
Code implementation:
/ / image stabilization
function debounce(fn, time) {
var timer;
return function() {
if (timer) {
clearTimeout(timer)
timer = null;
}
timer = setTimeout(() = > {
clearTimeout(timer)
timer = null; fn(); }, time); }}/ / throttling
function throttle(fn, time) {
var timer;
return function() {
if (timer) return;
timer = setTimeout(() = > {
clearTimeout(timer)
timer = null; fn(); }, time); }}Copy the code
15. Benefits of requestAnimationFrame
RequestAnimationFrame does not need to specify an interval. It takes a system interval, typically 60 frames per second, refreshed every 16ms. Benefits:
- Commit all updates in one reflux to improve performance
- RequestAnimationFrame also stops rendering when the page is inactive, and resumes when activated
Pros and cons of the virtual DOM
Advantages:
- Ensure the performance lower limit. Manipulating the real DOM structure is very expensive, and the virtual DOM uses JS objects to simulate the real DOM, reducing the cost of manipulating the DOM structure at the logical level
- You don’t need to manipulate the actual DOM. With two-way data binding, the nodes in the DOM structure update automatically when the data changes, without us having to handle it manually
- High portability, good cross-platform. Whether it is Vue, React, weex, etc., we can see the virtual DOM, through their respective rendering base to render dom structure
Disadvantages:
- Extreme optimization cannot be carried out: Although reasonable optimization of virtual DOM+ is sufficient to meet the performance requirements of most applications, targeted extreme optimization cannot be carried out in some applications with extremely high performance requirements.
Arrow function
The arrow function has several uses with caution.
(1) The this object inside the function is the object at which it is defined, not the object at which it is used.
(2) Cannot be used as a constructor, that is, cannot use the new command, otherwise an error will be thrown.
(3) You can’t use arguments. This object does not exist in the function body. If you do, use the REST argument instead.
(4) Yield cannot be used, so arrow functions cannot be used as Generator functions.
The first one is very important, so let’s take a look at it through a few examples combined with the “this” reference above
function Timer() {
this.s1 = 0;
this.s2 = 0;
// Arrow function
setInterval(() = > this.s1++, 1000);
// A normal function
setInterval(function () {
this.s2++;
}, 1000);
}
var timer = new Timer();
setTimeout(() = > console.log('s1: ', timer.s1), 3100);
setTimeout(() = > console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0
Copy the code
In the example above, we define a constructor Timer. There are two timers inside, the first using the arrow function and the second using the normal function. Then we define two delay functions outside to get the values of s1 and s2.
First: the arrow function’s this is the value of the object on which it was defined, i.e. Timer, after 3.1s, the result is 3,
And then the this of the normal function refers to the global object, so s2 of the timer is still 0.
This is fixed, not because the arrow function has a mechanism to bind this, but because the arrow function does not have its own this, so the inner this is the outer code block’s this. Because it does not have this, it cannot be used as a constructor.
Curry by hand
Coriating functions play an important role in functional programming, which is roughly similar to chain calls. By coriating functions, we can better separate the parameters to achieve function reuse, which can be seen through the following classic 🌰
function add(a, b) {
return a + b
}
let addTen = curry(add,10)
addTen(1)/ / 11
addTen(5)/ / 15
let res = curry(add,1.2)/ / 3
Copy the code
The add function above takes two arguments. Once we have codified it, we can take the arguments apart and pass them. We want to create a utility function, addTen, whose purpose is to pass in the number +10. Of course, there is no need for corylation to do this, but you can get a general idea of what corylation does
Now let’s write the Coriization function
If we pass in at least one parameter to the function, it will execute and return the result, otherwise it will return a function for later calls, so we can quickly implement a simple curry function
function curry(fn,... arg1){
returnarg1.length===fn.length? fn(... arg1):function(. arg2){ returnfn(... arg1,... arg2)} }Copy the code
Handwriting asynchronous serial, parallel
From front end interview common handwritten questions
// byte interview questions, implement an asynchronous addition
function asyncAdd(a, b, callback) {
setTimeout(function () {
callback(null, a + b);
}, 500);
}
// Solution
// 1. promisify
const promiseAdd = (a, b) = > new Promise((resolve, reject) = > {
asyncAdd(a, b, (err, res) = > {
if (err) {
reject(err)
} else {
resolve(res)
}
})
})
// 2. Serial processing
async function serialSum(. args) {
return args.reduce((task, now) = > task.then(res= > promiseAdd(res, now)), Promise.resolve(0))}// 3. Parallel processing
async function parallelSum(. args) {
if (args.length === 1) return args[0]
const tasks = []
for (let i = 0; i < args.length; i += 2) {
tasks.push(promiseAdd(args[i], args[i + 1] | |0))}const results = await Promise.all(tasks)
returnparallelSum(... results) }/ / test
(async() = > {console.log('Running... ');
const res1 = await serialSum(1.2.3.4.5.8.9.10.11.12)
console.log(res1)
const res2 = await parallelSum(1.2.3.4.5.8.9.10.11.12)
console.log(res2)
console.log('Done'); }) ()Copy the code
Deep copy
Note:
- Object loop reference handling
- Date, re, etc
- Array, object processing
function deepCopy(obj, cache = new WeakMap(a)) {
if (typeofobj ! = ='object' || obj === null) return obj
/ / date of regular packing and class object reference https://blog.csdn.net/liwusen/article/details/78759373
if ({}.toString.call(obj) === '[object Date]') return new Date(obj.valueOf())
if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)
// Prevent circular references
if (cache.get(obj)) return cache.get(obj)
const res = Array.isArray(obj) ? [] : {}
cache.set(obj, res)
for (const key in obj) {
if (typeof obj[key] === 'object') {
res[key] = deepCopy(obj[key], cache)
} else {
res[key] = obj[key]
}
}
return res
}
/ / test
const source = {
name: 'Jack'.meta: {
age: 12.birth: new Date('1997-10-10'),
ary: [1.2, { a: 1}].say() {
console.log('Hello')
}
}
}
source.source = source
const newObj = deepCopy(source)
console.log(newObj.meta.birth == source.meta.birth)
Copy the code
20, handwritten random generation of hexadecimal color and RGB interconversion
function generateRamdomColor() {
const dic = []
for (let i = 0; i < 16; i++) {
dic.push(i.toString(16))}const type = [3.6]
let count = random(type)
return (
The '#' +
(function tt(res) {
return (res += random(dic)) && res.length === count ? res : tt(res)
})(' '))}function random(arr) {
return arr[Math.floor(Math.random() * arr.length)]
}
function hex2rgb(hex) {
const str = hex.slice(1),
res = []
if (str.length === 3) {
for (const w of str) {
res.push(parseInt(w + w, 16))}}else {
for (let i = 0; i < 6; i += 2) {
res.push(parseInt(str[i] + str[i + 1].16))}}return res
}
let color = generateRamdomColor()
let rgb = hex2rgb(color)
console.log(color) //random hex color
console.log(rgb)
Copy the code
21. BOM location.hash & History
Hash and history are the apis of BOM, and they correspond to location. Hash and history. Let’s discuss the differences between the two.
locatoin.hash
That’s the hash of the URL,#
Followed by zero or more characters, nothing is an empty string
- This hash value represents a state of affairs for the client, and the hash part will not be sent when the request is sent to the server
- Hash changes do not trigger page changes
- A hash change adds a record to the browser
- You can listen for hash changes through the hasChange event
History
The history object represents the navigation history of the user since the current window was first used
History is often used to create forward and back buttons, and if the URL changes (including hash changes), a history is added. This behavior is often used by spAs to simulate forward and back, because it does not trigger a page refresh.
History uses the state management API to change the URL without triggering a page refresh, such as pushState, which adds a record to the history and changes the URL. Other than that, the browser page does not send a request to the server, but it does send a request when you manually refresh.
Note that it is important to ensure that each “fake” URL created with pushState corresponds to a real physical URL on the server, otherwise a manual refresh will result in 404
Why do you need server configuration in History mode?
In normal access to a.b.com, there is no problem, the server directly load the corresponding directory in the index.html file, but there is a difference between the server can not find the corresponding directory, 404 will appear, so we need to configure the access path of the child path in Nginx, SPA, Just point to index.html
The hash value behind the hash mode is only the state and will not be sent to the server after the request, so the hash mode can be used safely, but if you care about the appearance level, you need to use the History mode to configure the corresponding value