This week’s interview questions:
- What is a closure? What does a closure do?
- Implement the promise.all method
- What are the methods for loading JS scripts asynchronously?
- Implement a flattening deep function that flattens a nested array
- What are the characteristics of an iterable?
What is a closure? What does a closure do?
What is a closure?
A closure is a function that has access to variables in the scope of another function, and the most common way to create a closure is to create another function inside a function.
Create a closure
function foo() {
var a = 2;
return function fn() {
console.log(a); }}let func = foo();
func(); 2 / / output
Copy the code
Closures allow functions to continue to access the lexical scope at definition time. Thanks to fn, foo’s inner scope is not destroyed after foo() is executed.
No matter how an inner function is passed outside its lexical scope, it holds a reference to the original definition scope and uses closures wherever the function is executed. Such as:
function foo() {
var a = 2;
function inner() {
console.log(a);
}
outer(inner);
}
function outer(fn){
fn(); / / closures
}
foo();
Copy the code
The role of closures
-
The ability to access the lexical scope in which a function is defined (preventing it from being recycled).
-
Privatization variable
function base() {
let x = 10; // Private variables
return {
getX: function() {
returnx; }}}let obj = base();
console.log(obj.getX()); / / 10
Copy the code
- Simulate block-level scopes
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = (function(j){
return function () {
console.log(j);
}
})(i);
}
a[6] ();/ / 6
Copy the code
- Create a module
function coolModule() {
let name = 'Yvette';
let age = 20;
function sayName() {
console.log(name);
}
function sayAge() {
console.log(age);
}
return {
sayName,
sayAge
}
}
let info = coolModule();
info.sayName(); //'Yvette'
Copy the code
The module pattern has two prerequisites (from JavaScript you Don’t Know)
- There must be an external enclosing function that must be called at least once (each call creates a new module instance)
- Enclosing functions must return at least one inner function so that the inner function can form a closure in the private scope and can access or modify the private state.
Disadvantages of closures
Closures cause the variables of a function to remain in memory, and too many closures can cause memory leaks
16. Implement the promise.all method
Before implementing the promise.all method, we must first know the function and characteristics of promise.all, because in clear Promise. All function and characteristics of the case, we can further write implementation.
Promise. All functions
Promise.all(iterable) returns a new Promise instance. This example will be a big pity if all promises in the iterable parameter are fulfilled or if no promise is included in the parameter, the state will become a big pity. If the promise parameter has a failed promise, the instance callback fails because of the return result of the first failed promise.
let p = Promise.all([p1, p2, p3]);
Copy the code
The state of P is determined by P1, P2 and P3, which can be divided into the following: There are two cases:
(1) Only when the states of P1, P2 and P3 become depressing, the state of P will become depressing. At this time, the return values of P1, P2 and P3 will form an array and be passed to the callback function of P.
(2) As long as p1, P2 and P3 are rejected, P becomes rejected, and the return value of the first rejected instance is passed to p’s callback function.
The characteristics of the Promise. All
Promise.all returns an instance of Promise
- If the argument passed is an empty iterable,
Promise.all
会 synchronousReturns a completed statepromise
- If the parameters passed in do not contain any promises,
Promise.all
会 asynchronousReturns a completed statepromise
- In other cases,
Promise.all
Returns aPendingThe state of thepromise
.
Promise.all Indicates the state of the returned Promise
- If all the promises in the parameters passed in become completed,
Promise.all
The returnedpromise
Asynchronously become complete. - If one of the parameters passed in is
promise
Failure,Promise.all
Asynchronously give the failed result to the failed state callback, regardless of the restpromise
Whether or not complete - In any case,
Promise.all
The returnedpromise
The result of the completion state is an array
Promise. All implementations
Consider only the case where the argument passed is an array
/** Only consider promises to pass in arrays */
Promise.all = function (promises) {
return new Promise((resolve, reject) = > {
if (promises.length === 0) {
resolve([]);
} else {
let result = [];
let index = 0;
for (let i = 0; i < promises.length; i++ ) {
// Consider that I may be a thenable object or a normal value
Promise.resolve(promises[i]).then(data= > {
result[i] = data;
if (++index === promises.length) {
// This is a big pity. // All promises will become a big pity
resolve(result);
}
}, err => {
reject(err);
return; }); }}}); }Copy the code
You can use the code on the MDN for testing
Consider iterable objects
Promise.all = function (promises) {
/** Promises is an iterable, omit argument types */
return new Promise((resolve, reject) = > {
if (promises.length === 0) {
// If the argument passed is an empty iterable
return resolve([]);
} else {
let result = [];
let index = 0;
let j = 0;
for (let value of promises) {
(function (i) {
Promise.resolve(value).then(data= > {
result[i] = data; // Ensure the order
index++;
if (index === j) {
// where j is length.
resolve(result);
}
}, err => {
// A promise fails
reject(err);
return;
});
})(j)
j++; //length}}}); }Copy the code
Test code:
let p2 = Promise.all({
a: 1[Symbol.iterator]() {
let index = 0;
return {
next() {
index++;
if (index == 1) {
return {
value: new Promise((resolve, reject) = > {
setTimeout(resolve, 100.'foo');
}), done: false}}else if (index == 2) {
return {
value: new Promise((resolve, reject) = > {
resolve(222);
}), done: false}}else if(index === 3) {
return {
value: 3.done: false}}else {
return { done: true}}}}}}); setTimeout((a)= > {
console.log(p2)
}, 200);
Copy the code
17. What are the methods for loading JS scripts asynchronously?
<script>
Add to the tagasync
(it) ordefer
(html4) property, the script will load asynchronously.
<script src=".. /XXX.js" defer></script>
Copy the code
The difference between defer and Async is that:
defer
Wait until the entire page is properly rendered in memory (DOM structure is fully generated, and other scripts are executed), before window.onload;async
Once the download is complete, the rendering engine interrupts the rendering, executes the script, and continues rendering.- If there are more than one
defer
Scripts are loaded in the order they appear on the page - multiple
async
The script does not guarantee the load order
Dynamically createscript
The label
A dynamically created script with SRC set will not be downloaded, but will be added to the document before the JS file is downloaded.
let script = document.createElement('script');
script.src = 'XXX.js';
// Add it to the HTML file to start the download
document.body.append(script);
Copy the code
XHR asynchronously loads JS
let xhr = new XMLHttpRequest();
xhr.open("get"."js/xxx.js".true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
eval(xhr.responseText); }}Copy the code
18. Implement a flattening deep function that flattens a nested array
Using the Array. The prototype. Flat
ES6 added the flat method for array instances, which “flattens” nested arrays into one-dimensional arrays. This method returns a new array with no effect on the original array.
Flat flattens only one layer by default. If you want to “flatten” a nested array of multiple layers, you need to pass flat an integer indicating the number of layers you want to flatten.
function flattenDeep(arr, deepLength) {
return arr.flat(deepLength);
}
console.log(flattenDeep([1[2[3[4]], 5]], 3));
Copy the code
When the integer passed is greater than the number of nested layers of the array, the array is flattened into a one-dimensional array. The maximum number that JS can represent is math.pow (2, 53) -1, so we can define a flattening deep function like this
function flattenDeep(arr) {
// Of course, most of the time we don't have this many levels of nesting
return arr.flat(Math.pow(2.53) - 1);
}
console.log(flattenDeep([1[2[3[4]], 5]]));
Copy the code
Utilize reduce and concat
function flattenDeep(arr){
return arr.reduce((acc, val) = > Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
}
console.log(flattenDeep([1[2[3[4]], 5]]));
Copy the code
Use Stack to unnest multiple layers of nested arrays indefinitely
function flattenDeep(input) {
const stack = [...input];
const res = [];
while (stack.length) {
// Use pop to fetch and remove values from the stack
const next = stack.pop();
if (Array.isArray(next)) {
// Use push to send back elements in the inner array without changing the original inputstack.push(... next); }else{ res.push(next); }}// Use reverse to restore the order of the original array
return res.reverse();
}
console.log(flattenDeep([1[2[3[4]], 5]]));
Copy the code
19. What are the characteristics of iterable
ES6 specifies that the default Iterator interface is deployed in the symbol. Iterator property of the data structure. Iterator a data structure is considered iterable as long as it has the symbol. iterator property (the Symbol. Iterator method corresponds to the iterator generator and returns an iterator object).
Characteristics of an iterable
- with
Symbol.iterator
Properties,Symbol.iterator()
Returns an traverser object - You can use
for ... of
cycle
let arry = [1.2.3.4];
let iter = arry[Symbol.iterator]();
console.log(iter.next()); //{ value: 1, done: false }
console.log(iter.next()); //{ value: 2, done: false }
console.log(iter.next()); //{ value: 3, done: false }
Copy the code
The native hasIterator
Interface data structure:
- Array
- Map
- Set
- String
- TypedArray
- The arguments object for the function
- The NodeList object
Define an iterable
An object is iterable only if it has the correct symbol. iterator property. Therefore, we can make the object iterable by adding symbol. iterator.
let obj = {
name: "Yvette".age: 18.job: 'engineer', * [Symbol.iterator]() {
const self = this;
const keys = Object.keys(self);
for (let index = 0; index < keys.length; index++) {
yield self[keys[index]];Yield expressions can only be used in Generator functions}}};for (var key of obj) {
console.log(key); //Yvette 18 engineer
}
Copy the code
Reference article:
[1] MDN Promise.all
[2] Promise
[3] Iterator
Thank you for your precious time to read this article. If this article gives you some help or inspiration, please do not spare your praise and Star. Your praise is definitely the biggest motivation for me to move forward. Github.com/YvetteLau/B…