Ok,
1. What are the basic types? Is NULL an object? What is the difference between basic data type and complex data type storage?
- There are six basic types, respectively is undefined, null, Boolean, string, number, symbol (new) ES6.
- Although Typeof NULL returns the value object, NULL is not an object, but one of the basic data types.
- Basic data types are stored in stack memory, which stores values.
- Complex data types are stored in the heap memory, where addresses are stored. When we assign an object to another variable, we copy the address, pointing to the same memory space, and when one object changes, the other changes.
2. Is typeof correct? Instanceof? How does Instanceof work?
First of all, Typeof can correctly determine the basic data type, but in addition to null, Typeof NULL output is an object.
However, in the case of objects, typeof cannot correctly determine their type. Typeof a function can print ‘function’, and everything else is printed with object. In this case, we cannot know exactly the typeof the object.
Instanceof can accurately judge complex data types, but cannot correctly judge basic data types.
Instanceof is determined by the chain of prototypes. A instanceof B is determined by the chain of prototypes. If A is found at the top of the chain of prototypes (null; Object.prototype.__proto__), which is still not equal to B.protototype, then return false, otherwise return true.
Instanceof: instanceof
// L instanceof R
functionInstance_of (L, R) {var O = r.prototype; // take the explicit stereotype of R L = L.__proto__; // Take the implicit prototype of Lwhile (true) {
if(L === null) // The top level is foundreturn false;
if(O === L) // Returns if O is strictly equal to Ltrue
return true; L = L.__proto__; // Continue to search up the prototype chain}}Copy the code
3. For of, for in, and forEach,map
- for… Of loop: if you have an iterator interface, you can use for… The of loop iterates over its members (attribute values). for… Scopes that the of loop can use include arrays, Set and Map structures, some array-like objects, Generator objects, and strings. for… The of loop calls the iterator interface, which returns only properties with numeric indexes for arrays. For ordinary objects, for… The “of” structure cannot be used directly and will cause errors. You must deploy the Iterator interface before using it. You can break the loop.
- for… In loop: enumerable that iterates over the object itself and its inheritance
attribute
The attribute value cannot be obtained directly. You can break the loop.
- ForEach: can only traverse the array, cannot interrupt, no return value (or think the return value is undefined).
- Map: can only traverse the array, not interrupt, return a modified array.
PS: object.keys () : returns all enumerables of a given Object
I wrote some code to test the question of whether forEach would change the array (note the complex data types). With the exception of forEach, apis like Map have the same problem.
letarry = [1, 2, 3, 4]; arry.forEach((item) => { item *= 10; }); console.log(arry); //[1, 2, 3, 4] arry.forEach((item) => { arry[1] = 10; // Manipulate the array directly}); console.log(arry); //[1, 10, 3, 4]let arry2 = [
{ name: "Yve"}, { age: 20 } ]; arry2.forEach((item) => { item.name = 10; }); console.log(arry2); //[ { name: 10 }, { age: 20, name: 10 } ]Copy the code
4. How to tell if a variable is an array?
- Use array. isArray. If true is returned, it is an Array
- Use instanceof Array. If true is returned, it is an Array
- Use Object. The prototype. ToString. Call judgment, if the value is [Object Array], that is an Array
- Judging by constructor, if it is an array, then
arr.constructor === Array
. (Not exactly, because we can specifyobj.constructor = Array
)
function fn() {
console.log(Array.isArray(arguments)); //false; Console. log(array.isarray ([1,2,3,4])); console.log(array.isarray ([1,2,3,4])); //trueconsole.log(arguments instanceof Array); / / false to the console. The log ([1, 2, 3, 4] instanceof Array); //trueconsole.log(Object.prototype.toString.call(arguments)); / / / the object the Arguments] the console. The log (object. The prototype. ToString. Call ([1, 2, 3, 4])); //[object Array] console.log(arguments.constructor === Array); //false
arguments.constructor = Array;
console.log(arguments.constructor === Array); //true
console.log(Array.isArray(arguments)); //false} fn (1, 2, 3, 4);Copy the code
5. What’s the difference between a class array and an array?
Class array:
1) Has the length attribute and the other attributes (index) are non-negative integers (the index in the object is treated as a string);
2) Do not have methods that arrays have;
A class Array is a normal object, whereas a real Array is of type Array.
Common class array are: arugments function parameters, the DOM object list (such as through the document. QuerySelectorAll get list), the jQuery object ($(” div “), for example).
Class arrays can be converted to arrays:
/ / the first Array. Prototype. Slice. Call (arrayLike, start); // The second method [...arrayLike]; Array.from(arrayLike);Copy the code
PS: Any object that defines an Iterator interface can be turned into a true array using the extension operator.
The array. from method is used to turn two types of objects into true arrays: array-like objects and iterable objects.
What’s the difference between == and ===?
=== Does not need to be cast; returns true only if the types are the same and the values are equal.
== If the two types are different, you need to cast them first. The specific process is as follows:
- First check whether the two types are the same, and if so, check whether the values are equal.
- If the type is different, the conversion is performed
- Returns true if the comparison is null or undefined.
- Check whether the types are string and number. If so, convert the string to number
- Determine whether one of the parties is a Boolean. If so, convert Boolean to number and check again
- Determine if one of the parties is object and the other is string, number, or symbol. If so, convert the object to its original type
let person1 = {
age: 25
}
let person2 = person1;
person2.gae = 20;
console.log(person1 === person2); //trueNote that complex data types are compared to reference addressesCopy the code
[] ==! []
[] ==! [] Is true or false?
- First, we need to know! The priority is higher than == (more operator priorities can be viewed: Operator priorities)
! []
Reference type conversions to booleans are true, therefore! []
Is false- If one of the two parties is Boolean, convert Boolean to number. If false is false, convert Boolean to number. The corresponding value is 0.
- If there is only one number in the array, then convert an object to a number. If there is only one number in the array, then convert an object to a number. If there is only one number in the array, then convert a number to a NaN.
- 0 = = 0; To true
7. What are the differences between classes in ES6 and classes in ES5?
- All methods defined within an ES6 class are non-enumerable;
- ES6 classes must use new calls;
- There is no variable promotion in ES6 class.
- The ES6 class defaults to strict mode.
- ES6 class subclasses must call super() in the constructor of the superclass to have this object; In ES5, the relationship of class inheritance is reversed, with a subclass of this and then a superclass method applied to this.
8. Which array API will change the original array?
The API for modifying the original array is:
splice/reverse/fill/copyWithin/sort/push/pop/unshift/shift
The apis that do not modify the array are:
slice/map/forEach/every/filter/reduce/entries/find
Note: Each item in the array is of a simple data type, and the array is not directly manipulated.
9. What is the difference between a let, const, and var?
- Variables defined by let and const are not promoted, while variables defined by var are promoted.
- Let and const are block-level scopes in JS
- Let and const do not allow repeated declarations (throwing an error)
- Variables defined by let and const throw an error (creating a temporary dead zone) if used before the statement is defined, whereas var does not.
- Const declares a read-only constant. Once declared, the value of the constant cannot be changed (if the declaration is an object, the reference address of the object cannot be changed).
10. What is variable promotion in JS? What is a temporary dead zone?
Variable promotion is when variables can be used before they are declared, and the value is undefined.
A variable is not available (throwing an error) until it is declared with the let/const command within a block of code. Grammatically, this is called a “temporary dead zone.” Temporary dead zones also mean that Typeof is no longer a 100 percent safe operation.
typeof x; // ReferenceError(ReferenceError)let x;Copy the code
typeof y; // If the value is undefined, no error is reportedCopy the code
The essence of a temporary dead zone is that as soon as you enter the current scope, the variable you want to use already exists, but cannot be retrieved until the line of code that declares the variable appears.
11. How to judge this? What’s this for the arrow function?
There are four binding rules for this: default, implicit, explicit, and new.
- Is the function called in new (new binding)? If so, then this is bound to the newly created object.
- Is the function called by call,apply, or hard bind? If so, then this binds to the specified object.
- Whether the function is called in a context object (implicit binding), if so, this is bound to that context object. Usually obj. Foo ()
- If none of the above, use the default binding. If in strict mode, it is bound to undefined, otherwise to a global object.
- If null or undefined is passed to call, apply, or bind as the binding object to this, these values are ignored when called, and the default binding rules are applied.
- The arrow function does not have its own this; it inherits this from the previous code block.
Test whether you have successfully got this knowledge (browser execution environment):
var number = 5;
var obj = {
number: 3,
fn1: (function () {
var number;
this.number *= 2;
number = number * 2;
number = 3;
return function () {
var num = this.number;
this.number *= 2;
console.log(num);
number *= 3;
console.log(number);
}
})()
}
var fn1 = obj.fn1;
fn1.call(null);
obj.fn1();
console.log(window.number);Copy the code
12. The difference between lexical scope and this.
- Lexical scope is determined by where do you scope variables and blocks when you write code
- This is bound when it is called, and what this points to depends entirely on where the function is called.
Talk about your understanding of the JS execution context stack and scope chain.
Execution context is the environment in which the current JavaScript code is parsed and executed. JS execution context stack can be considered as a stack structure for storing function calls, following the principle of “first in, second out”.
- JavaScript executes on a single thread, and all code is queued.
- When the browser starts executing global code, it first creates a global execution context and pushes it to the top of the execution stack.
- Each entry into a function’s execution creates the function’s execution context and pushes it to the top of the execution stack. Current function execution – When complete, the current function execution context is off the stack and waits for garbage collection.
- The browser’s JS execution engine always accesses the execution context at the top of the stack.
- There is only one global context, which is off the stack when the browser is closed.
Scope chain: Both LHS and RHS queries start the search at the current scope, and if they are not found, the search continues to the upper scope, one scope at a time, until the global scope.
Was it difficult?
Keep up the challenge!
Know difficult, more to continue!
14. What are closures? What do closures do? What are the use scenarios for closures?
Closures are functions that have access to variables in the scope of another function. The most common way to create closures is to create a function inside another function.
Closures do the following:
- Encapsulating private variables
- Mimic block-level scope (there is no block-level scope in ES5)
- A module that implements JS
15. What’s the difference between call and apply? How are call, APLly and bind implemented internally?
Call and apply use the same function, but the difference is that the parameters are passed differently:
-
fn.call(obj, arg1, arg2, …) , calls a function with a specified value of this and a list of arguments supplied separately.
-
Fn. Apply (obj, [argsArray]), calls a function with a specified value of this and arguments provided as an array (or array-like object).
Call the core:
- Sets the function as the property of the argument passed in
- Specify this to the function and pass the given arguments to execute the function
- If no argument is passed or the argument is null, the default point is window/global
- Deletes the function on the argument
Function.prototype.call = function(context) {/** if the first argument passed is null or undefined, then point this to window/global */ /** If the first argument is not null or undefined, then point this to window/global */ /** So it must be an object */if(! Context) {// Context is null or undefined. Context = Typeof window ==='undefined'? global : window; } context.fn = this; // This refers to the current instance of the Function.letrest = [...arguments].slice(1); // Get the parameters other than the object to which this points, and return an empty array after sliceletresult = context.fn(... rest); // This refers to context.delete context.fn;returnresult; } var foo = {name:'Selina'
}
var name = 'Chirs';
function bar(job, age) {
console.log(this.name);
console.log(job, age);
}
bar.call(foo, 'programmer', 20);
// Selina programmer 20
bar.call(null, 'teacher', 25); // Browser environment: Chirs Teacher 25; Node environment: undefined teacher 25Copy the code
apply:
The implementation of Apply is similar to call, but note that the arguments are different. The second argument to apply is an array or an array of classes.
Function.prototype.apply = function (context, rest) {
if(! // Set the default value when context is null or undefined. Context = Typeof Window ==='undefined' ? global : window;
}
context.fn = this;
let result;
if(rest = = = undefined | | rest = = = null) {/ / undefined or null is not the Iterator object, cannot be... result = context.fn(rest); }else if(typeof rest === 'object') { result = context.fn(... rest); } delete context.fn;return result;
}
var foo = {
name: 'Selina'
}
var name = 'Chirs';
function bar(job, age) {
console.log(this.name);
console.log(job, age);
}
bar.apply(foo, ['programmer', 20]);
// Selina programmer 20
bar.apply(null, ['teacher', 25]); // Browser environment: Chirs programmer 20; Node environment: undefined teacher 25Copy the code
bind
There is an important difference between bind and call/apply. When a function is called, it is called directly, but bind creates a new function. When the new function is called, the first argument to bind() will be as this when it runs, and the following sequence of arguments will be passed as its arguments before the arguments passed.
Function.prototype.bind = function(context) {
if(typeof this ! = ="function"){
throw new TypeError("not a function");
}
let self = this;
let args = [...arguments].slice(1);
function Fn() {};
Fn.prototype = this.prototype;
let bound = function() {
let res = [...args, ...arguments]; //bindContext = this instanceof Fn? this : context || this;returnself.apply(context, res); } // string bound.prototype = new Fn();return bound;
}
var name = 'Jack';
function person(age, job, gender){
console.log(this.name , age, job, gender);
}
var Yve = {name : 'Yvette'};
let result = person.bind(Yve, 22, 'enginner') ('female'); Copy the code
16. How does new work? What’s the difference between creating an object as a new object and creating it as a literal?
new:
- Create a new object.
- The new object will be executed with the [[prototype]] connection.
- Assign the scope of the constructor to the new object, so that this refers to the new object.
- If the function returns no other object, then the function call in the new expression automatically returns the new object.
function new(func) {
let target = {};
target.__proto__ = func.prototype;
let res = func.call(target);
if (typeof(res) == "object" || typeof(res) == "function") {
return res;
}
return target;
}Copy the code
Literals create objects without calling the Object constructor, which is simpler and performs better.
The new Object() method is essentially a method call that involves traversing the method in the proto chain. When the method is found, the stack information necessary for the method call is produced, and the stack is freed when the method call is complete.
When an Object is defined through an Object literal, the Object constructor is not called.
17. What is your understanding of archetypes?
In JavaScript, whenever an object is defined (a function is also an object), the object contains predefined properties. Each of these function objects has a Prototype property that points to the function’s prototype object. The benefit of using prototype objects is that all object instances share the properties and methods it contains.
18. What is a prototype chain? What problem does the prototype chain solve?
The prototype chain solves the problem of inheritance.
Each object has a prototype object, and by proto (pronunciation: The dunder proto pointer points to its prototype Object and inherits methods and properties from it. The prototype Object may also have a prototype, so that it points to null(object.proptoType.__proto__ points to null). This relationship is known as the prototype chain, through which one object can own properties and methods defined in other objects.
(p.__proto__ === parent.prototype)
19. What is the difference between __proto__ and prototype?
Prototype is a property of the constructor.
__proto__ is a property that is available to every instance and has access to the [[prototype]] property.
The instance’s __proto__ and its constructor’s prototype point to the same object.
function Student(name) {
this.name = name;
}
Student.prototype.setAge = function(){
this.age=20;
}
let Jack = new Student('jack'); console.log(Jack.__proto__); //console.log(Object.getPrototypeOf(Jack));; console.log(Student.prototype); console.log(Jack.__proto__ === Student.prototype); //trueCopy the code
20. Implementing an inheritance using ES5?
Composite inheritance (most common inheritance)
function SuperType() {
this.name = name;
this.colors = ['red'.'blue'.'green'];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
console.log(this.age);
}Copy the code
Other inheritance methods, you can refer to “JavaScript Advanced programming”
21. What is a deep copy? What’s the difference between a deep copy and a shallow copy?
A shallow copy is a copy of only the first level object, but when an object’s property is of a reference type, the actual copy is the reference, which changes when the value to which the reference refers changes.
Deep copy copies the value of a variable. For a variable of a non-basic type, it recurses to a variable of a basic type before copying. Objects after a deep copy are completely isolated from the original ones. Modifications to one object do not affect the other.
Implement a deep copy:
functionDeepClone (obj) {// Recursive copyif(obj === null) returnnull; / / nullif(obj instanceof RegExp) return new RegExp(obj);
if(obj instanceof Date) return new Date(obj);
if(typeof obj ! = ='object') {// If it is not a complex data type, return it directlyreturnobj; } /** * constructor ();} /** constructor ();} /** constructor ();let t = new obj.constructor();
for(let key inT [key] = deepClone(obj[key]); }return t;
}Copy the code
Can’t watch? Others’
It will be yours
22. What’s the difference between shockproof and throttling? Shockproof and throttling implementation.
Both damping and throttling are used to prevent functions from being called multiple times. The difference is that, assuming a user fires the function all the time at intervals less than the set time, the function is called only once in the case of shaking stabilization and once every other time in the case of throttling.
Debounce: The function is executed only once in n seconds. If the high-frequency event is fired again within n seconds, the time is recalculated
function debounce(func, wait, immediate=true) {
lettimeout, context, args; Const later = () =>setTimeout(() => {// Delay function execution, clear the timer Timeout = null // In the case of delay execution, the function will execute in the delay function // use the previously cached arguments and contextif (!immediate) {
func.apply(context, args);
context = args = null;
}
}, wait);
let debounced = function(... params) {if(! timeout) { timeout = later();if(immediate) {// apply(this, params); }else{// closure context = this; args = params; }}else {
clearTimeout(timeout);
timeout = later();
}
}
debounced.cancel = function () {
clearTimeout(timeout);
timeout = null;
};
return debounced;
};Copy the code
Application scenarios of anti-shaking:
- Each resize/ Scroll event triggers statistical events
- Validation of text input (send AJAX requests to verify text input, once only)
Throttling: High-frequency events are executed only once in a specified period of time. After a one-time execution, they are executed twice only after the period is longer than the specified period.
//underscore.js
function throttle(func, wait, options) {
var timeout, context, args, result;
var previous = 0;
if(! options) options = {}; var later =function () {
previous = options.leading === false ? 0 : Date.now() || new Date().getTime();
timeout = null;
result = func.apply(context, args);
if(! timeout) context = args = null; }; var throttled =function () {
var now = Date.now() || new Date().getTime();
if(! previous && options.leading ===false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if(! timeout) context = args = null; }else if(! timeout && options.trailing ! = =false) {// Determine whether the timer and trailing Timeout = are setsetTimeout(later, remaining);
}
return result;
};
throttled.cancel = function () {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};
return throttled;
};
Copy the code
The application scenarios of function throttling are as follows:
- DOM element drag-and-drop implementation (Mousemove)
- Shooter mouseDown/KeyDown event (only one bullet fired per unit time)
- Calculate the distance the mouse moves (mousemove)
- Canvas (Mousemove)
- Search lenovo (Keyup)
- Automatically load more: After adding debounce to scroll, the scroll will determine whether the bottom of the page is reached only after the user stops scrolling. If you are throttling, you will judge it once in a while as long as the page scrolls
23. Select the maximum value of array (ES5, ES6)
Math.max. Apply (null, [14, 3, 77, 30]); // ES6 math.max (... [14, 3, 77, 30]); / / reduce,3,77,30 [14]. Reduce ((accumulator, currentValue) = > {return accumulator = accumulator > currentValue ? accumulator : currentValue
});Copy the code
24. What are the new features of ES6?
- New block-level scope (let,const)
- Provides syntactic sugar to define classes
- – Added a basic data type (Symbol)
- Added deconstructive assignment to variables
- Function parameters are allowed to be set to default values, rest parameters are introduced, and arrow functions are added
- There are new apis for arrays, such as the isArray/from/of method; Array instances add methods such as entries(), keys(), and values()
- Extended operators have been added for objects and arrays
- – Modular (import/export) has been added to ES6
- ES6 added Set and Map data structures
- ES6 provides a Proxy constructor to generate Proxy instances
- – Added Generator and Iterator in ES6
25. Why do setTimeout countdown errors occur?
SetTimeout () simply inserts the event into the “task queue”. The main thread will not execute the callback function until the current code (execution stack) has finished executing. If the current code takes a long time, or it might, there is no guarantee that the callback will execute at the time specified by setTimeout(). So, the second argument to setTimeout() represents the minimum time, not the exact time.
The HTML5 standard specifies that the second argument to setTimeout() must have a minimum value of at least 4 milliseconds, and if it is below that, the default is 4 milliseconds. Before that. Older browsers set the minimum time to 10 milliseconds. In addition, for those DOM changes (especially those involving re-rendering of the page), they are usually performed at 16-millisecond intervals. RequestAnimationFrame () works better than setTimeout();
26. Why 0.1 + 0.2! = 0.3?
0.1 + 0.2! = 0.3 is due to accuracy loss during base conversion and advanced operations.
Here’s how:
JavaScript uses the Number type to represent numbers (both integer and floating point) and 64 bits to represent a Number.
Photo caption:
- Bit 0th: Sign bit, 0 for positive, 1 for negative (s)
- Rank 1 to 11: Storage Index Section (E)
- Places 12 through 63: store the fractional part (that is, the significant number) f
Computers cannot directly operate on decimal numbers. They need to convert them into binary numbers according to the IEEE 754 specification, and then operate on the order.
1. Base conversion
0.1 and 0.2 will loop indefinitely when converted to binary
0.1 - > 0.0001100110011001... 0.2 -> 0.0011001100110011... (infinite loop) Copy the codeCopy the code
However, due to the IEEE 754 mantail digit limit, the following redundant bits need to be truncated, so the accuracy has been lost in the conversion between bases.
2. Calculate the order
Because the number of exponential bits is different, the operation of order may also cause precision loss.
Following the above two steps (including the loss of precision), the final result is
0.0100110011001100110011001100110011001100110011001100
The result is 0.30000000000000004 when converted to decimal.
27. There are several states of promise. What are the advantages and disadvantages of a promise?
This is very depressing. There are three kinds of promise: fulfilled, rejected or pending.
Promise’s advantages:
- Once the state changes, it doesn’t change again, and it can happen any time
- Asynchronous operations can be expressed as synchronous operations, avoiding layers of nested callback functions
Promise’s shortcomings:
- Can’t cancel Promise
- When you are in the pending state, you have no way of knowing what stage you are currently in
28. Do Promise constructors execute synchronously or asynchronously, and methods in THEN? How to implement PROMISE then processing?
The Promise constructor executes synchronously. Methods in then are executed asynchronously.
Promise then realize,
29. What’s the difference between Promise and setTimeout?
Promise is a microtask and setTimeout is a macro task. In the same event loop, promise.then is always executed before setTimeout.
30. How to implement promise.all?
To implement Promise.all, we first need to know what promise.all does:
- If the passed argument is an empty iterable, then the promise object callback is resolved. In this case, the promise object is executed synchronously, and the rest is returned asynchronously.
- Return an asynchronous completion if the passed argument contains no promises. Promises are “done” or if there are no promises in the argument.
- If one of the parameters fails, then the Promise object returned by promise. all fails
- In any case, the result of the completion state of the Promise returned by promise. all is an array
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let index = 0;
let result = [];
if (promises.length === 0) {
resolve(result);
} else {
function processValue(i, data) {
result[i] = data;
if(++index === promises.length) { resolve(result); }}for (leti = 0; i < promises.length; Resolve (promises[I]). Then ((data) => {processValue(I, data); //promises[I] can be promises. }, (err) => { reject(err);return; }); }}}); } copy codeCopy the code
31. How to realize promise.finally?
Whether you succeed or fail, you will go to the finally. The value is passed unchanged to the following then.
Promise.prototype.finally = function (callback) {
return this.then((value) => {
return Promise.resolve(callback()).then(() => {
return value;
});
}, (err) => {
returnPromise.resolve(callback()).then(() => { throw err; }); }); } copy codeCopy the code
32. What is function currying? Implementing sum(1)(2)(3) returns the sum of 1,2,3
Function currying is the technique of turning a function that takes multiple arguments into one that takes a single argument (the first argument of the original function) and returns a new function that takes the remaining arguments and returns the result.
function sum(a) {
return function(b) {
return function(c) {
returna+b+c; } } } console.log(sum(1)(2)(3)); // 6 Copy the codeCopy the code
Extension: Implement a curry function, currying a normal function:
function curry(fn, args = []) {
return function() {let rest = [...args, ...arguments];
if (rest.length < fn.length) {
return curry.call(this,fn,rest);
}else{
returnfn.apply(this,rest); / /}}}test
function sum(a,b,c) {
return a+b+c;
}
letsumFn = curry(sum); console.log(sumFn(1)(2)(3)); //6 console.log(sumFn(1)(2, 3)); / / 6Copy the code
This article is reprinted from the front little sister
The next
1. Talk about the history of JS asynchrony
The earliest solutions to asynchrony are callback functions, such as event callbacks, and callbacks in setInterval/setTimeout. One common problem with callbacks, however, is the callback hell problem (illustrated later);
To solve the problem of callback hell, the community came up with the Promise solution, which WAS written into the language standard by ES6. Promise solves the problem of callback hell, but promises also have some problems, such as errors that can’t be tried and caught, and using a Promise chained call doesn’t solve the problem of callback hell at all, just a different way of writing it.
The Generator function is introduced in ES6. It is an asynchronous programming solution. The Generator function is the implementation of coroutine in ES6. Use the yield statement. But generators are more complex to use.
Async is the syntactic sugar of Generator functions. Async /await makes asynchronous code look like synchronous code. The goal of asynchronous programming is to make asynchronous logic look like synchronous code.
1. Callback function
Fs. readFile(XXX, XXX)'utf-8'.function(err, data) {
//code
});Copy the code
Use scenarios of callback functions (including but not limited to):
- Event callback
- Node API
- Callback function in setTimeout/setInterval
Asynchronous callback nesting makes code difficult to maintain and inconvenient to handle errors in A uniform way, unable to try catch and callback hell (such as reading A text, then reading B based on A text, then reading C based on B…). .
fs.readFile(A, 'utf-8'.function(err, data) {
fs.readFile(B, 'utf-8'.function(err, data) {
fs.readFile(C, 'utf-8'.function(err, data) {
fs.readFile(D, 'utf-8'.function(err, data) {
//....
});
});
});
});Copy the code
2.Promise
Promises mainly address the problem of callback hell. Promises were first proposed and implemented by the community, and ES6 wrote them into the language standard, unifying usage, and providing Promise objects natively.
Let’s take a look at how Promise solves the callback hell problem, using the readFile example above as an example.
function read(url) {
return new Promise((resolve, reject) => {
fs.readFile(url, 'utf8', (err, data) => {
if(err) reject(err);
resolve(data);
});
});
}
read(A).then(data => {
return read(B);
}).then(data => {
return read(C);
}).then(data => {
return read(D);
}).catch(reason => {
console.log(reason);
});Copy the code
To run the Code to see how it works, poke (VS Code Runner to execute the Code): github.com/YvetteLau/B…
Think about how you handled asynchronous concurrency before Promise, assuming you had a requirement to read the contents of three files and output the final result when all were read successfully. Once you have a Promise, what do you do with it? Code can be stamped: github.com/YvetteLau/B…
Note: You can use Bluebird to promisethe interface;
What are the advantages and problems of Promise?
3.Generator
The Generator function is an asynchronous programming solution provided by ES6. The entire Generator function is an encapsulated asynchronous task, or a container of asynchronous tasks. Where an asynchronous operation needs to be paused, the yield statement is used to indicate this.
Generator functions are typically used with yields or promises. The Generator function returns an iterator. If you are not familiar with generators and iterators, please learn the basics. Let’s look at a simple use of the Generator:
function* gen() {
let a = yield 111;
console.log(a);
let b = yield 222;
console.log(b);
let c = yield 333;
console.log(c);
let d = yield 444;
console.log(d);
}
lett = gen(); // The next method can take an argument, which is treated as the return value of the previous yield expression. // The first time we call next, we pass an invalid argument t.ext (2); / / a output 2; t.next(3); / / b output 2; t.next(4); / / c output 3; t.next(5); / / d output 3;Copy the code
To give you an idea of how the above code works, I draw a diagram of each call to the next method:
Using the Generator + CO library as the readFile example above:
const fs = require('fs');
const co = require('co');
const bluebird = require('bluebird');
const readFile = bluebird.promisify(fs.readFile);
function* read() {
yield readFile(A, 'utf-8');
yield readFile(B, 'utf-8');
yield readFile(C, 'utf-8');
//....
}
co(read()).then(data => {
//code
}).catch(err => {
//code
});
Copy the code
How to do this without using the CO library? Can you write your own my_co? Please stamp: github.com/YvetteLau/B…
PS: If you are not familiar with Generator/ Yield, it is recommended to read the ES6 documentation.
4.async/await
The concept of async/await is introduced in ES7. Async is a syntactic sugar that combines Generator functions and automatic actuators (Co) in a single function.
The advantage of async/await is that the code is clean and can handle callback hell without having to write as many then chains as Promise. Errors can be tried and caught.
Using the Generator + CO library as the readFile example above:
const fs = require('fs');
const bluebird = require('bluebird');
const readFile = bluebird.promisify(fs.readFile);
async function read() {
await readFile(A, 'utf-8');
await readFile(B, 'utf-8');
await readFile(C, 'utf-8');
//code
}
read().then((data) => {
//code
}).catch(err => {
//code
});Copy the code
Executable code, please poke: github.com/YvetteLau/B…
Think about how async/await handles asynchronous concurrency. Github.com/YvetteLau/B…
If you have a better answer or idea, please leave a comment on this topic on Github: Talk about the history of JS asynchrony
2. Talk about the understanding of async/await. How to implement async/await?
Async /await is the syntax-sugar of the Generator, making asynchronous operations easier. Here’s a comparison:
The async function replaces the asterisk (*) of Generator functions with async and yields with await.
We say async is the syntax sugar of Generator, but what’s sweet about this sugar?
1) Async function is a built-in executor. After the function is called, it will be automatically executed and output the final result. The Generator needs to call next or be used in conjunction with the CO module.
2) Better semantics. Async and await are clearer than asterisks and yields. Async means that there are asynchronous operations in the function, and await means that the expression immediately following it needs to wait for the result.
3) Wider applicability. The co module convention is that yield can only be followed by a Thunk function or a Promise object, whereas async can be followed by the await command by a Promise object and the value of the primitive type.
4) The return value is Promise. The async function returns a Promise object, and the Generator returns an Iterator. Promise objects are more convenient to use.
The principle of the async function is to package the Generator function and the automatic actuator in one function.
If you want to know how to write my_co step by step, you can use github.com/YvetteLau/B…
function my_co(it) {
return new Promise((resolve, reject) => {
function next(data) {
try {
var { value, done } = it.next(data);
}catch(e){
return reject(e);
}
if (!done{/ /donefortrue// The value is not necessarily a Promise, but may be a normal value. Wrapper with promise.resolve. Promise.resolve(value).then(val => { next(val); }, reject); }else{ resolve(value); } } next(); // Run the next}); }function* test() {
yield new Promise((resolve, reject) => {
setTimeout(resolve, 100);
});
yield new Promise((resolve, reject) => {
// throw Error(1);
resolve(10)
});
yield 10;
return 1000;
}
my_co(test()).then(data => { console.log(data); // Output 1000}).catch((err) => {console.log()'err: ', err);
});Copy the code
If you have a better idea or answer, please leave a comment on github.
3. What should I notice when using async/await?
- The result may be rejected, which is the same as the Promise object returned by async. So you need to add error handling. You can add catch methods to promises after each await; You can also place await code in
try... catch
In the. - Async operations following multiple await commands are better off firing at the same time if no secondary relationship exists.
Async = async; // Async = asyncfunction f1() {
await Promise.all([
new Promise((resolve) => {
setTimeout(resolve, 600);
}),
new Promise((resolve) => {
setTimeout(resolve, 600); })])} // Asyncfunction f2() {
let fn1 = new Promise((resolve) => {
setTimeout(resolve, 800);
});
let fn2 = new Promise((resolve) => {
setTimeout(resolve, 800);
})
await fn1;
await fn2;
}Copy the code
- Await command can only be used with async functions. If used with normal functions, an error will be reported.
- The async function can keep the run stack.
/** * function a runs an asynchronous task b() internally. Function a() does not break while b() is running, but continues execution. * By the time b() is finished, a() may have already finished * the context in which b() is running has disappeared. * If b() or c() reports an error, the error stack will not include a(). * /function b() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 200)
});
}
function c() { throw Error(10); } const a = () => { b().then(() => c()); }; a(); Const m = async () => {await b(); c(); }; m();Copy the code
The async function can keep the run stack.
If you have a better answer or idea, please leave a comment on github for this topic: What should I be aware of when using async/await?
4. How to implement promise.race?
Before code implementation, we need to understand promise.race’s features:
-
Promise.race still returns a Promise with the same state as the first completed Promise. It can be successful or it can be rejects, depending on what the first Promise is.
-
If the argument passed is not iterable, an error will be thrown.
-
If the parameter array passed is empty, then the returned promise will wait forever.
-
If the iteration contains one or more non-committed values and/or resolved/rejected commitments, promise.race resolves to the first value found in the iteration.
Promise.race = function(promises) {//promises must be a traversable data structure, otherwise they are thrown wrongreturn new Promise((resolve, reject) => {
if(typeof promises[Symbol.iterator] ! = ='function') {// true is not the error promise.reject ()'args is not iteratable! ');
}
if (promises.length === 0) {
return;
} else {
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then((data) => {
resolve(data);
return;
}, (err) => {
reject(err);
return; }); }}}); }Copy the code
Test code:
Promise.race([]).then((data) => {console.log())'success ', data);
}, (err) => {
console.log('err ', err); }); Promise.race().then((data) => {console.log();'success ', data);
}, (err) => {
console.log('err ', err);
});
Promise.race([
new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),
new Promise((resolve, reject) => { setTimeout(() => { resolve(200) }, 200) }),
new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) })
]).then((data) => {
console.log(data);
}, (err) => {
console.log(err);
});Copy the code
B: Promise. All/Promise. Reject/Promise. Resolve/Promise. The prototype. Finally/Promise. Prototype. Catch the principle, if you are not too will, stamp: Promise the source code to achieve
If you have a better answer or idea, please leave a comment on github: How to implement Promise.race?
5. What are the characteristics of traversable data structures?
For an object to be available for… Iterator interface called by the of loop must deploy the Iterator generation method on its Symbol. Iterator property (or objects on the prototype chain have the method)
PS: The fundamental feature of a traverser object is that it has a next method. Each time the next method is called, it returns an information object representing the current member, with the value and done properties.
// Add an Iterator interface to an object;let obj = {
name: "Yvette",
age: 18,
job: 'engineer',
[Symbol.iterator]() {
const self = this;
const keys = Object.keys(self);
let index = 0;
return {
next() {
if (index < keys.length) {
return {
value: self[keys[index++]],
done: false
};
} else {
return { value: undefined, done: true}; }}}; }};for(let item of obj) {
console.log(item); //Yvette 18 engineer
}Copy the code
The Symbol. Iterator method is abbreviated using the Generator function (the iterator object generation function), which can be abbreviated as follows:
let obj = {
name: "Yvette",
age: 18,
job: 'engineer',
* [Symbol.iterator] () {
const self = this;
const keys = Object.keys(self);
for (letindex = 0; index < keys.length; index++) { yield self[keys[index]]; // Yield expressions can only be used in Generator functions}}};Copy the code
The data structure with the native Iterator interface is as follows.
- Array
- Map
- Set
- String
- TypedArray
- The Arguments object for the function
- The NodeList object
- Entries ()/keys()/values() are deployed in the array, Set, and Map of ES6, all of which return an iterator object.
If you have a better answer or idea, please leave a comment on this topic on Github: What are the features of traversable data structures?
6. What is the difference between requestAnimationFrame and setTimeout/setInterval? What are the benefits of using requestAnimationFrame?
Before requestAnimationFrame, we mainly used setTimeout/setInterval to animate JS.
The key to writing animation is to set the interval between loops. On the one hand, the interval between loops is short enough to make the animation look smooth and smooth. On the other hand, the loop spacing should be long enough to ensure that the browser has the ability to render the resulting changes.
Most computer monitors have a refresh rate of 60 Hz, or 60 redraws per second. Most browsers limit redrawing operations to the frequency at which the display redraws, because the user experience does not improve beyond that. Therefore, the best loop interval for the smoothest animation is 1000ms / 60, which is about 16.7ms.
One notable drawback of setTimeout/setInterval is that the time is imprecise. SetTimeout /setInterval only guarantees that the delay or interval is not less than the set time. Because they’re actually just adding tasks to the task queue, but they have to wait if the previous task hasn’t completed yet.
RequestAnimationFrame is the system time interval to maintain the best rendering efficiency, not because the interval is too short, resulting in excessive drawing overhead; Also won’t because the interval time is too long, the use of animation card is not smooth, so that a variety of web animation effects can have a unified refresh mechanism, thus saving system resources, improve system performance, improve visual effects.
In summary, requestAnimationFrame has the following advantages over setTimeout/setInterval when writing animations:
1. RequestAnimationFrame does not need to set the time, using the system time interval, can achieve the best animation effect.
2. RequestAnimationFrame aggregates all DOM operations in each frame in a single redraw or backflow.
When requestAnimationFrame() is running in a background TAB or hidden
RequestAnimationFrame (try using requestAnimationFrame to write A moving ball from A to B):
function step(timestamp) {
//code...
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
Copy the code
What’s the difference between requestAnimationFrame and setTimeout/setInterval? What are the benefits of using requestAnimationFrame?
7. What are the rules for casting JS types?
I can’t explain the rules of type conversion in a few words. I really want to cry out
JS type conversion is divided into compulsory type conversion and implicit type conversion.
-
Cast with Number(), parseInt(), parseFloat(), toString(), String(), Boolean().
-
Logical operators, &&, | |,!) , operators (+, -, *, /), relational operators (>, <, <=, >=), equality operators (==), or if/while conditions may be implicitly cast.
Cast
1.Number() converts an argument of any type to a numeric type
The rules are as follows:
- If Boolean, true and false are converted to 1 and 0, respectively
- If it’s a number, it returns itself
- If null, return 0
- If undefined, return
NAN
- If it is a string, follow these rules:
- If the string contains only numbers (or
0X
/0x
A string of hexadecimal digits beginning with a positive or negative sign is allowed), which is converted to decimal - If the string contains a valid floating point format, convert it to a floating point number
- If it is an empty string, convert it to 0
- If the string is not in the above format, return it
NaN
- If the string contains only numbers (or
- If Symbol is used, an error is thrown
- If it is an object, the object’s
valueOf()
Method, and then convert the returned value according to the previous rule. If the result of the transformation isNaN
, the object’stoString()
Method, again following the previous rule to convert the returned string value.
Some built-in objects call the default valueOf behavior:
object | The return value |
---|---|
Array | The array itself (object type) |
Boolean | Boolean value (primitive type) |
Date | The number of milliseconds elapsed from midnight January 1, 1970 UTC to the encapsulated date |
Function | Function itself (object type) |
Number | Numeric value (primitive type) |
Object | The object itself (object type) |
String | String value (primitive type) |
Number('0111'); //111
Number('0X11') //17
Number(null); //0
Number(' '); //0
Number('1a'); //NaN Number(-0X11); / / - 17Copy the code
2.parseInt(param, radix)
If the first argument passed is a string type:
- Ignore the whitespace before the string until the first non-null character is found, and return NaN if it is an empty string
- Returns NaN if the first character is not a number or a positive or negative sign
- If the first character is a numeric/sign, the parse continues until the string is parsed or a non-numeric symbol is encountered
If the first argument passed in is of type Number:
- If the number starts with 0, it is parsed as if it were octal (if it is an octal number); If it starts with 0x, it is resolved as if it were hexadecimal
If the first argument is null or undefined, or an object type:
- Returns NaN
If the first argument is an array: 1. Go to the first element of the array and parse as above
If the first argument is of type Symbol: 1. Throws an error
If the RADIX parameter is specified, radix is used for parsing
parseInt('0111'); //111 parseInt(0111); // Octal 73 parseInt(' '); //NaN parseInt('0X11'); //17
parseInt('1a') //1
parseInt('a1'); //NaN
parseInt(['10aa'.'aaa']); //10 parseInt([]); //NaN; parseInt(undefined);Copy the code
parseFloat
The rule is basically the same as parseInt. It takes a Number type or a string. If it is in a string, only the first decimal point is valid.
toString()
The rules are as follows:
- If it is of type Number, output a numeric string
- If it’s null or undefined, throw it
- If it is an array, expand the array to output. Empty array, return
' '
- If it is an object, return
[object Object]
- In the case of Date, return a literal representation of the Date
- If it is a function, output the corresponding string (demo below)
- If Symbol, print the Symbol string
let arry = [];
let obj = {a:1};
let sym = Symbol(100);
let date = new Date();
let fn = function() {console.log('Hold on, we can win! ')}
let str = 'hello world';
console.log([].toString()); // ' 'console.log([1, 2, 3, undefined, 5, 6].toString()); / / 1, 2, 3, 5, 6 console. The log (' arry. The toString ()); / / 1, 2, 3. The console log (obj. ToString ()); // [object Object] console.log(date.toString()); // Sun Apr 21 2019 16:11:39 GMT+0800 (CST) console.log(fn.toString()); //function () {console.log('Hold on, we can win! ')} console.log(str.toString()); //'hello world'console.log(sym.toString()); // Symbol(100) console.log(undefined.toString()); / / throw the wrong console. The log (null. The toString ()); / / wrongCopy the code
String()
The conversion rules for String() are basically the same as for toString(). The biggest difference is that null and undefined are used. Null and undefined are the strings ‘null’ and ‘undefined’.
Boolean
Null, false, ”, 0(including +0, -0), and NaN convert to false.
Implicit type conversion
, &&, | |,! , if/while
You need to convert data to a Boolean type. The conversion rules are the same as for Boolean casting
Operator: + – * /
The + operator can be used not only to add numbers but also to concatenate strings.
Only if both sides of the plus sign are numbers, you’re adding. If both sides are strings, concatenate without implicit type conversions.
In addition to the above, if the operand is an object, a number, or a Boolean, the toString() method is called to get the string value (the toString conversion rule). For undefined and NULL, an explicit conversion to a String is called with String(), respectively, followed by concatenation.
console.log({}+10); //[object Object]10 console.log([1, 2, 3, undefined, 5, 6] + 10); / / 1, 2, 3, 5610Copy the code
The -, *, and/operators are for operations, and if one of the operation values is not a Number, the Number() function is implicitly called for conversion. If one of them is converted to NaN, the result is NaN.
Relational operators: ==, >, <, <=, >=
>, <, <=, >=
- If both operation values are numeric, a numeric comparison is made
- If both operation values are strings, the character encoding values corresponding to the strings are compared
- If one of the parties is of type Symbol, an error is thrown
- In all cases other than the above, Number() is cast and then compared.
Note: NaN is a very special value, it is not equal to any type of value, including itself, and it returns false when compared to any type of value.
console.log(10 > {}); / / return false. / * * * {} in the valueOf - > {} * {} in the toString () -- - >'[object Object]'--> NaN *NaN and any type than size, returnfalse* /Copy the code
The equality operator: ==
- If the types are the same, no type conversion is required.
- If one of the operators is null or undefined, then the other operator must be null or undefined to return true, otherwise both operators will return false.
- Return false if one of them is of type Symbol.
- If the two operation values are string and number, the string is converted to number
- If an operation value is Boolean, then convert to number
- If an operation has a valueOf object and the other is a string, number, or symbol, it will convert the object to its original type. (call the valueOf/toString method on object to convert it.)
How is an object converted to its original data type
If the [Symbol. ToPrimitive] interface is deployed, then calling this interface returns an error if it does not return the underlying data type.
If the [Symbol. ToPrimitive] interface is not deployed, return the valueOf valueOf(), and toString() if it is not of the base type, or raise an exception if it is not.
// Call valueOf first, then toStringlet obj = {
[Symbol.toPrimitive]() {
return 200;
},
valueOf() {
return 300;
},
toString() {
return 'Hello'; } // If valueOf does not return a basic datatype, then toString is called. // If toString does not return a basic datatype, then console.log(obj + 200) is thrown; / / 400Copy the code
If you have a better answer or idea, please leave a comment on github: What are the rules of JS casting?
8. Describe your understanding of webWorker?
HTML5 proposes the Web Worker standard, indicating that JS allows multi-threading, but the child threads are fully controlled by the main thread and cannot operate DOM. Only the main thread can operate DOM, so JS is still a single-threaded language in essence.
Web worker is to open a child thread on the basis of JS single-thread execution for program processing without affecting the execution of the main thread. When the child thread finishes execution, it will return to the main thread, and the execution of the main thread will not be affected in this process. Subthread and main thread provide data interaction interface postMessage and onMessage, to send and receive data.
var worker = new Worker('./worker.js'); // Create a child thread worker.postMessage('Hello');
worker.onmessage = function(e) { console.log(e.data); //Hi worker.terminate(); // End the thread;Copy the code
//worker.js
onmessage = function (e) {
console.log(e.data); //Hello
postMessage("Hi"); // Send a message to the main process};Copy the code
This is just the simplest example code, and projects typically run some time-consuming code in child threads.
If you have a better answer or idea, feel free to leave a comment on this topic on Github: briefly describe your understanding of Webworkers
9. What are the differences between ES6 and CommonJS modules?
-
When an ES6 module is compiled, it determines the dependencies of the module, as well as the variables in and out of the module.
CommonJS module, loaded at runtime.
-
ES6 modules automatically adopt strict mode, regardless of whether the module header says “Use strict”. What are the restrictions of strict mode? / / links)
-
Require can do dynamic loading. Import statements cannot. Import statements must be in the top-level scope.
-
The top-level “this” in the ES6 module refers to undefined, and the top-level “this” in the ommonJS module refers to the current module.
-
The CommonJS module outputs a copy of the value, and the ES6 module outputs a reference to the value.
The CommonJS module outputs a copy of the value, which means that once a value is printed, changes within the module cannot affect it. Such as:
//name.js
var name = 'William';
setTimeout(() => name = 'Yvette', 200);
module.exports = {
name
};
//index.js
const name = require('./name');
console.log(name); //William
setTimeout(() => console.log(name), 300); //WilliamCopy the code
Compare this to the ES6 module:
The ES6 module operates differently from CommonJS. When the JS engine statically analyzes the script, it generates a read-only reference when it encounters the module loading command import. When the script actually executes, the value is then applied to the loaded module based on the read-only reference.
//name.js
var name = 'William';
setTimeout(() => name = 'Yvette', 200);
export { name };
//index.js
import { name } from './name';
console.log(name); //William
setTimeout(() => console.log(name), 300); //YvetteCopy the code
If you have a better answer or idea, please leave a comment on github: What are the differences between ES6 and CommonJS modules?
10. What is the mechanism of browser event proxy?
Before we talk about how browser event proxies work, let’s first look at the concept of event flow. Early browsers, IE used event capture event flow, and Netscape used event capture. “DOM2 event” divides the event flow into three phases, the capture phase, the target phase and the bubble phase. Modern browsers also follow this specification.
So what is the event proxy?
Event agent is also called event delegate, which binds an event to the ancestor DOM element. When the event of the descendant DOM element is triggered, the principle of event bubble is used to trigger the event bound to the ancestor DOM. Because events are bubbling up from the target element to the Document object.
Why event proxies?
-
The number of events added to a page affects the performance of the page. If too many events are added, the performance of the page deteriorates. Using event agent can greatly reduce the number of registered events.
-
When an event proxy is used, a descendant element is added dynamically and does not need to be event bound again.
-
Instead of worrying that an event-registered DOM element may not be able to reclaim its event handler if it is removed, we can avoid this problem by delegating the event handler to a higher-level element.
For example, proxy all click events in the page to the document:
AddEventListener accepts three arguments: the name of the event to be processed, the function that handles the event, and a Boolean value. The Boolean value defaults to false. Indicates that the event handler is called during the bubble phase or, if set to true, during the capture phase.
document.addEventListener('click'.function(e) { console.log(e.target); /** * Capture phase call call event handler, eventPhase is 1; * At the target, eventPhase is 2 * Calls the event handler in the bubble phase, eventPhase is 1; */ console.log(e.eventPhase); });Copy the code
If you have a better idea or answer, please leave a comment on github: How does the browser event proxy work?
How to customize events in js?
Custom DOM events (regardless of pre-IE9 versions)
There are three ways to customize an Event: new Event(), createEvent(‘CustomEvent’), and new CustomEvent ().
- use
new Event()
Failed to get event.detail
let btn = document.querySelector('#btn');
let ev = new Event('alert', {
bubbles: true, // Whether the event bubbles; The default valuefalse
cancelable: true, // Can the event be cancelled; The default valuefalse
composed: false
});
btn.addEventListener('alert'.function (event) {
console.log(event.bubbles); //true
console.log(event.cancelable); //true
console.log(event.detail); //undefined
}, false);
btn.dispatchEvent(ev);Copy the code
- use
createEvent('CustomEvent')
(DOM3)
To create a CustomEvent, call createEvent(‘CustomEvent’) and return an object with an initCustomEvent method that takes the following four arguments:
- Type: string indicating the type of event fired, such as ‘alert’
- Bubbles: Boolean: indicates whether the event bubbles
- Cancelable: A Boolean value indicating whether the event can be cancelled
- Detail: Any value stored in the detail attribute of the Event object
let btn = document.querySelector('#btn');
let ev = btn.createEvent('CustomEvent');
ev.initCustomEvent('alert'.true.true.'button');
btn.addEventListener('alert'.function (event) {
console.log(event.bubbles); //trueconsole.log(event.cancelable); //true
console.log(event.detail); //button
}, false); btn.dispatchEvent(ev); Copy the codeCopy the code
- use
new customEvent()
(DOM4)
More convenient to use than createEvent(‘CustomEvent’)
var btn = document.querySelector('#btn'); /* * The first argument is the event type * the second argument is an object */ var ev = new CustomEvent('alert', {
bubbles: 'true',
cancelable: 'true',
detail: 'button'
});
btn.addEventListener('alert'.function (event) {
console.log(event.bubbles); //trueconsole.log(event.cancelable); //true
console.log(event.detail); //button
}, false);
btn.dispatchEvent(ev);Copy the code
Custom non-DOM events (Observer mode)
The EventTarget type has a separate property, Handlers, that stores event handlers (observers).
AddHandler () is used to register an event handler for a given type of event;
Fire () is used to fire an event;
RemoveHandler () is used to log off an event handler for an event type.
function EventTarget(){
this.handlers = {};
}
EventTarget.prototype = {
constructor:EventTarget,
addHandler:function(type,handler){
if(typeof this.handlers[type= = ="undefined"){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
fire:function(event){
if(! event.target){ event.target = this; }if(this.handlers[event.type] instanceof Array){
const handlers = this.handlers[event.type];
handlers.forEach((handler)=>{
handler(event);
});
}
},
removeHandler:function(type,handler){
if(this.handlers[type] instanceof Array){
const handlers = this.handlers[type];
for(var i = 0,len = handlers.length; i < len; i++){
if(handlers[i] === handler){
break; } } handlers.splice(i,1); }}} // UsefunctionhandleMessage(event){ console.log(event.message); } // Create a new object var target = new EventTarget(); // Add an event handler target.addHandler("message", handleMessage); // Fires the event target.fire({type:"message", message:"Hi"}); //Hi // remove the event handler target.removeHandler("message",handleMessage); // Fires the event again with no event handler target.fire({type:"message",message: "Hi"});Copy the code
If you have better answers or ideas, please leave a comment on this topic on Github: How can JS customize events?
12. What are the cross-domain approaches? How does it work?
Browsers have the same origin policy. Only when the “protocol”, “domain name”, and “port number” are the same, can they be called same-origin. One of the differences is that they are cross-domain.
So what does the same origin policy do? The same origin policy restricts how a document or script loaded from one source interacts with a resource from another source. This is an important security mechanism for isolating potentially malicious files.
So why do we need to cross domains? First, the front-end and server are deployed separately, and the interface requests need to cross domains. Second, we may load the pages of other websites as embedded iframe.
What are the cross-domain approaches?
Common cross-domain approach
- jsonp
Although browsers have the same origin policy, the SRC attribute of the
Implementation principle:
Step1: Create the callback method
Step2: Insert the script tag
Step3: The background receives the request, parses the callback method passed by the front end, returns the call to the method, and passes the data as parameters to the method
Step4: The front end executes the method call returned by the server
The following code is intended only to illustrate the principles of JSONP; use a mature library in your project. Take a look at a simple implementation on the front end and the server side:
// Front end codefunction jsonp({url, params, cb}) {
returnNew Promise((resolve, reject) => {// create script tagslet script = document.createElement('script'); // Hang the callback function on the window window[cb] =function(data) { resolve(data); / / code execution, delete the insert script tags. The document body. RemoveChild (script). } // The callback function added to the requested address params = {... params, cb} //wb=b&cb=showlet arrs = [];
for(let key in params) {
arrs.push(`${key}=${params[key]}`);
}
script.src = `${url}?${arrs.join('&')}`; document.body.appendChild(script); }); } / / usefunction sayHi(data) {
console.log(data);
}
jsonp({
url: 'http://localhost:3000/say',
params: {
//code
},
cb: 'sayHi'
}).then(data => {
console.log(data);
});Copy the code
//express starts a backend servicelet express = require('express');
let app = express();
app.get('/say', (req, res) => {
let{cb} = req.query; // Get the name of the callback function passed. Cb is key res.send('${cb}('Hello! ') `); }); app.listen(3000);Copy the code
From today on, you’ll know how JSONP works
- cors
Jsonp can only support GET requests, cORS can support multiple requests. Cors does not require much work on the front end.
Simple cross-domain request:
As long as the access-control-allow-Origin Header set by the server matches the request source, the browser allows cross-domains
- The request method is get, head, or POST.
- Content-type is a value in application/x-www-form-urlencoded, multipart/form-data, or Text /plain, or not set, The default is application/ X-www-form-urlencoded.
- There is no custom HTTP header in the request, such as x-token. Accept, accept-language, content-language, last-event-id, content-type
// Simple cross-domain request app.use((req, res, next) => {res.setheader ('Access-Control-Allow-Origin'.'XXXX');
});Copy the code
A cross – domain request with a Preflighted request
Those dissatisfied with simple cross-domain requests are cross-domain requests with precheck. The server needs to set access-control-allow-Origin, access-control-allow-Methods, and access-control-allow-headers (Allowed header)
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin'.'XXX');
res.setHeader('Access-Control-Allow-Headers'.'XXX'); // Allow the return header res.setheader ('Access-Control-Allow-Methods'.'XXX'); // Allow the put method to request the interface res.setheader ('Access-Control-Max-Age', 6); // Precheck survival timeif(req.method === "OPTIONS") { res.end(); // If method is OPTIONS, do not handle it}});Copy the code
More about CORS can be accessed: HTTP Access Control (CORS)
- Nginx reverse proxy
Using nGINx reverse proxy to implement cross-domain, you only need to modify the configuration of NGINx to solve cross-domain problems.
When web site A requests an interface from web site B, it sends A request to web site B. Nginx receives the request according to the configuration file and makes the request to Web site B instead of Web site A. Nginx gets this resource and returns it to website A to solve the cross-domain problem.
For example, the nginx port number is 8090 and the server port number to be requested is 3000. (localhost:8090 request localhost:3000/say)
The nginx configuration is as follows:
server {
listen 8090;
server_name localhost;
location / {
root /Users/liuyan35/Test/Study/CORS/1-jsonp;
index index.html index.htm;
}
location /say {
rewrite ^/say/(.*)$ /The $1 break;
proxy_pass http://localhost:3000;
add_header 'Access-Control-Allow-Origin' The '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
}
# others
}Copy the code
- websocket
Websocket is a persistent PROTOCOL for HTML5, which enables full-duplex communication between browsers and servers, and is also a cross-domain solution.
Websocket is not affected by the same origin policy. As long as the server supports the Websocket, it supports cross-domain without any configuration.
The front-end page is on port 8080.
let socket = new WebSocket('ws://localhost:3000'); // The protocol is ws socket.onopen =function() {
socket.send('Hi, how are you');
}
socket.onmessage = function(e) {
console.log(e.data)
}Copy the code
3000 port on the server. As you can see, webSocket does not need to do cross-domain configuration.
let WebSocket = require('ws');
let wss = new WebSocket.Server({port: 3000});
wss.on('connection'.function(ws) {
ws.on('message'.function(data) { console.log(data); // Receive the message from the page'Hi, how are you'
ws.send('Hi'); // Send a message to the page}); });Copy the code
- postMessage
PostMessage is used as a cross-domain before the front page, such as the parent page and the iframe page. The window.postMessage method, which allows communication across Windows, regardless of whether the two Windows are homologous.
In my work, I only used it twice. The first time was to send postMessage information in H5 page, which was received in ReactNative WebView and handled accordingly. Another is a ropeable page. A ropeable page uses an iframe page. To resolve the sliding event conflict, the iframe page listens for gestures and sends a message telling the parent whether to swipe left or right.
The child page sends messages to the parent page
The parent page
window.addEventListener('message', (e) => {
this.props.movePage(e.data);
}, false);Copy the code
Child pages (iframe) :
if(/ * left sliding * /) {window. The parent & & window. The parent. PostMessage (1,The '*')}else if(/ * right * /) {window. The parent & & window. The parent. PostMessage (1,The '*')}Copy the code
The parent page sends messages to the child page
The parent page:
let iframe = document.querySelector('#iframe');
iframe.onload = function() {
iframe.contentWindow.postMessage('hello'.'http://localhost:3002');
}Copy the code
Child pages:
window.addEventListener('message'.function(e) {
console.log(e.data);
e.source.postMessage('Hi', e.origin); // return the message});Copy the code
- The node middleware
Cross-domain principle of Node middleware and Nginx proxy cross-domain, same-origin policy is restricted by the browser, and the server does not have same-origin policy.
Node middleware implements cross-domain principles as follows:
1. Accept the client request
2. Forward the request to the server.
3. Get the server response data.
4. Forward the response to the client.
The cross-domain approach is not commonly used
The following three cross-domain methods are rarely used, if you are interested, you can consult the relevant information.
-
window.name + iframe
-
location.hash + iframe
-
Document.domain (same for primary domain)
If you have a better answer or idea, please leave a comment on this topic on Github: What are the cross-domain approaches? How does it work?
13. What are the methods of js asynchronous loading?
The
The difference between defer and async is that: Defer does not execute until the entire page has been properly rendered in memory;
Once async is downloaded, the rendering engine will interrupt rendering and continue rendering after executing the script. Defer is “defer after rendering” and async is “execute as soon as download”.
If there are multiple Defer scripts, they will be loaded in the order in which they appear on the page.
Multiple async scripts do not guarantee loading order.
- Insert script script dynamically
function downloadJS() {
varelement = document.createElement("script");
element.src = "XXX.js"; document.body.appendChild(element); } // When to call the above methodCopy the code
- Conditional dynamic creation of scripts
So after onload the page,
If you have a better idea or answer, please leave a comment on github: What are the ways to load js asynchronously?
14. In what case does the following code A print 1?
/ /?if(a == 1 && a == 2 && a == 3) { console.log(1); } copy codeCopy the code
1. During type conversion, we know how an object is converted to its original data type. If [Symbol. ToPrimitive] is deployed, the return value of Symbol. ToPrimitive is returned. Of course, we can also deploy this function on the valueOf or toString interfaces, and the effect is the same.
// Use closures to extend scopelet a = {
[Symbol.toPrimitive]: (function() {
let i = 1;
return function() {
returni++; }}}) ()Copy the code
(1). [Symbol. ToPrimitive] is called when a == 1. [Symbol. ToPrimitive] == 2; [Symbol. ToPrimitive] = 3;
2. Define property A on window/global using object.definepropert. When obtaining property A, get will be called.
let val = 1;
Object.defineProperty(window, 'a', {
get: function() {
returnval++; }}); Copy the codeCopy the code
3. Take advantage of the array feature.
Var a = [1, 2, 3]; a.join = a.shift;Copy the code
The toString method of an array returns a string consisting of the return value of toString() for each element in the array joined (separated by commas) by calling the join() method.
Therefore, we can re-join the method. Return the first element and remove it.
If you have a better answer or idea, please leave a comment below on github: In what cases does code A print a 1?
15. What is the output of the following code?
function Foo() {
getName = function() {console.log(1)};
return this;
}
Foo.getName = function() {console.log(2)};
Foo.prototype.getName = function() {console.log(3)};
var getName = function() {console.log(4)};
function getName() {console.log(5)};
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();Copy the code
** explanation: ** a classic interview questions, only to help you review the knowledge, deepen understanding, real work, it is impossible to write code like this, otherwise, will certainly be killed.
1. During the precompilation phase, variable declarations and function declarations are promoted to the top of their corresponding scope.
So the above code compiles as follows (function declarations take precedence over variable declarations):
function Foo() {
getName = function() {console.log(1)};
return this;
}
var getName;
function getName() {console.log(5)};
Foo.getName = function() {console.log(2)};
Foo.prototype.getName = function() {console.log(3)};
getName = function() {console.log(4)};Copy the code
2.Foo.getName(); Call the getName method on Foo directly, and output 2
3.getName(); Output 4, getName is reassigned
4.Foo().getName(); Foo() is executed, and the window’s getName is reassigned, returning this; In the browser environment, non-strict mode, this points to window, this.getName(); The output of 1.
In strict mode, this refers to undefined, which throws an error.
In a node environment, this refers to global, and the node global variable is not hung on global, because global.getName corresponds to undefined, not function, and an error is thrown.
5.getName(); The nature that has already thrown wrong cannot move this step; Continue the browser in non-strict mode; Window. getName is reassigned and called again. The output is 1
6.new Foo.getName(); New has no list of arguments, and the corresponding priority is 18; Member access operator., which corresponds to a priority of 19. So it’s new (foo.getName)(); The new operator executes the method in the constructor, so the output here is 2.
7. New Foo (). The getName (); New takes the argument list, which corresponds to priority 19, and the member access operator. The priority is the same. Sibling operators that are evaluated in order from left to right. New Foo() initializes Foo(); Foo(); Foo(); Foo(); Foo(); Foo()
8.new new Foo().getName(); New takes a list of arguments and has priority 19, so it is equivalent to new (new Foo()).getName(); Initialize Foo’s instantiated object, and then use the getName function on its prototype as the constructor again, printing 3
So the final result is as follows:
Foo.getName(); //2 getName(); //4 Foo().getName(); //1 getName(); //1 new Foo.getName(); //2 new Foo().getName(); //3 new new Foo().getName(); / / 3Copy the code
If you have a better answer or idea, please leave a comment on github: What is the output of this code?
16. How is the implementation of two-way binding Proxy compared with Object. DefineProperty?
-
Object.definedproperty hijacks the properties of an Object. It hijacks the getter and setter methods of the properties to perform specific operations when the properties of the Object change. The Proxy hijacks the whole object.
-
Proxy returns a Proxy Object, and we only need to manipulate the new Object, whereas object.defineProperty can only be modified directly by traversing the Object properties.
-
Object.definedproperty does not support arrays, or more specifically, the array apis, because it is possible to hijack arry[I] = value, but it does not make much sense. Proxy can support various apis for arrays.
-
Although Object. DefineProperty has many drawbacks, it is more compatible than Proxy.
PS: Ve2. X uses Object. DefineProperty to realize bidirectional data binding, while V3.0 uses Proxy.
/ / the interceptorlet obj = {};
let temp = 'Yvette';
Object.defineProperty(obj, 'name', {
get() {
console.log("Read successful");
return temp
},
set(value) {
console.log("Setting successful"); temp = value; }}); obj.name ='Chris';
console.log(obj.name);Copy the code
PS: Object. DefineProperty: Object. DefineProperty: Object.
So we can see that the Proxy is going to hijack the whole object, it’s going to read properties in the object or it’s going to change property values, so it’s going to be hijacked. However, it is important to note that complex data types monitor the reference address, not the value, and if the reference address has not changed, then the set will not be triggered.
let obj = {name: 'Yvette', hobbits: ['travel'.'reading'], info: {
age: 20,
job: 'engineer'
}};
letP = new Proxy(obj, {get(target, key) {'Read successful');
return Reflect.get(target, key);
},
set(target, key, value) {
if(key === 'length') return true; // If the array length changes, return. console.log('Setting successful');
returnReflect.set([target, key, value]); }}); p.name = 20; P.age = 20; // Set succeeded; You do not need to define this property in advance p.hobbit.push ('photography'); // Read successfully; P.inofo. age = 18; // Read successfully; Setting does not trigger successCopy the code
Finally, let’s look at the array hijacking, the difference between object.definedProperty and Proxy
Object.definedproperty can hijack the index of an array as a property, but it only supports direct manipulation of arry[I]. It does not support the array API.
let arry = []
Object.defineProperty(arry, '0', {
get() {
console.log("Read successful");
return temp
},
set(value) {
console.log("Setting successful"); temp = value; }}); arry[0] = 10; Arry. push(10); // Cannot be hijackedCopy the code
Proxy can listen to array changes and support various apis. Note that changes to the array may trigger get and set more than once. If necessary, decide whether to process them based on the key value.
let hobbits = ['travel'.'reading'];
let p = new Proxy(hobbits, {
get(target, key) {
// if(key === 'length') return true; // If the array length changes, return. console.log('Read successful');
return Reflect.get(target, key);
},
set(target, key, value) {
// if(key === 'length') return true; // If the array length changes, return. console.log('Setting successful');
returnReflect.set([target, key, value]); }}); P.ice (0,1) // triggers get andset, can be hijacked'photography'); // Trigger get andsetp.slice(1); // Trigger get; Slice does not modify the arrayCopy the code
If you have any better answers or ideas, please leave a comment on github: How does implementing a bidirectional Proxy compare to Object. DefineProperty?
17. What is the difference between object.is () and the comparison operators ===, ==?
In the following cases, object. is considers equality
Both values are undefined and both values are null and both values are nulltrueOr arefalseThe two values are strings of the same number of characters in the same order and they point to the same object and they're both numbers and they're both positive zero plus zero and they're both negative zero minus zero and they're both NaN and they're the same number other than zero and NaNCopy the code
Object.is() is similar to ===, but with a few subtle differences, as follows:
- NaN is the same as NaN
- Minus 0 and plus 0 are not equal
console.log(Object.is(NaN, NaN)); //trueconsole.log(NaN === NaN); //false
console.log(Object.is(-0, +0)); //false
console.log(-0 === +0); //trueCopy the code
Object.is is far from ==, and == needs to be cast when the type is different, as explained in detail above.
This article is reproduced fromWinter job season you must understand the native JS