This collection aims to consolidate the core basic skills of front-end engineers, starting from the high frequency handwriting problems of various sizes of factories, summarizing the common simulation implementation, stick to learning, there will be Easter.
More detailed and more source code on github project, a problem containing a variety of solutions, interested in learning can download, long-term update and maintenance.
PS: If the article is wrong, please do not hesitate to correct. Your “praise” is the motivation of the author’s creation.
Part II: “Middle and advanced front-end interview” handwritten code collection (2)
Let’s start with the mind map:
1.instanceof
Instanceof determines whether the left prototype exists in the right prototype chain.
If the final prototype is null, it proves that it does not exist in the prototype chain, otherwise it does exist.
function myInstanceof(left, right) {
if (typeofleft ! = ='object' || left === null) return false // The base type is always false
let proto = Object.getPrototypeOf(left) // Get the prototype of the object
while(true) {
if (proto === null) return false
if (proto === right.prototype) return true
proto = Object.getPrototypeOf(proto)
}
}
Copy the code
2.Object.create
const myCreate = function(obj) {
function F() {}
F.prototype = obj
return new F() // Create a pure object that inherits the obj stereotype properties
}
Copy the code
3.new
When new is called it does three things:
- Give instance objects access to private properties
- Give instance objects access to properties on the constructor. Prototype chain
- Consider the case where the constructor has a return value
function myNew(ctor, ... args) {
let fn = Array.prototype.shift.call(arguments) // Take the first argument ctor
if (typeoffn ! = ='function') throw `${fn} is not a constructor`
let obj = Object.create(fn.prototype)
let res = fn.apply(obj, args) // If the constructor returns a value, execute it directly
let isObject = typeof res === 'object'&& res ! = =null
let isFunction = typeof res === 'function'
return isObject || isFunction ? res : obj
}
Copy the code
4.call & apply
Implementation idea: use who call a function, function this points to who this feature to achieve.
Function.prototype.myCall = function() { / / myApply gm
if (typeof this! = ='function') throw 'caller must be a function'
let self = arguments[0] | |window
self._fn = this
let args = [...arguments].flat().slice(1) // Expand to get the parameter list
letres = self._fn(... args)// The function's this refers to whoever calls the function
Reflect.deleteProperty(self, '_fn') // Delete the _fn attribute
return res
}
Copy the code
5.bind
Bind is used to change the this pointer and return a function.
Function.prototype.myBind = function() {
if (typeof this! = ='function') throw 'caller must be a function'
let self = this
let context = arguments[0]
let args = Array.prototype.slice.call(arguments.1)
let fn = function() {
let fnArgs = Array.prototype.slice.call(arguments)
// Arguments to bind + arguments to delay
self.apply(this instanceof self ? this : context, args.concat(fnArgs)
)
}
fn.prototype = Object.create(self.prototype) // Maintain the prototype
return fn
}
Copy the code
6. Mr Currie
Corey:
- Definition: Returns a new function after binding a function to a subset of its arguments.
- Benefits: Reduces code redundancy, increases readability, and is a concise way to implement function delegation.
Take a simple 🌰 :
function multiFn(x, y, z) {
return x * y * z
}
function curry() {... }// Suppose we have a curry function that can be currified
let multi = curry(multiFn)
multi(2.3.4)
multi(2) (3) (4)
multi(2.3) (4)
multi(2) (3.4) // All of the above results are equal to 3.
let seniorMulti = multi(2) // seniorMulti can be used multiple times
seniorMulti(3) (4) // We can do this if we feel that passing argument 2 repeatedly is always redundant.
Copy the code
Code implementation:
function curry(fn, args=[]) {
return function() {
let newArgs = args.concat(Array.prototype.slice.call(arguments))
if (newArgs.length < fn.length) { // If: the number of arguments < the number of parameters
return curry.call(this, fn, newArgs)
} else {
return fn.apply(this, newArgs)
}
}
}
// ES6 high appearance level writing
const curry = fn= >
judge = (. args) = >args.length === fn.length ? fn(... args) :(arg2) = >judge(... args, arg2)Copy the code
Parasitic combinatorial inheritance
function Parent() {
this.favorite = 'money'
}
function Child() {
Parent.call(this) // Inherits the attributes of the parent class
this.age = 18
}
Child.prototype = Object.create(Parent.prototype) // Inherits the stereotype attributes of the parent class
Object.setPrototypeOf(Child, Parent) // Inherits a static method from the parent class
Child.prototype.constructor = Child // constructor redirect to Child
Copy the code
8. How the Class private property in TypeScript works
Private attributes generally satisfy:
- Can be accessed by different methods inside the class, but not outside the class
- Subclasses cannot inherit private attributes from their parent class
ES6 already provides a # for us to use, but requires a Babel translation, so let’s implement one ourselves.
const MyClass = (function() { // Utilize closures and WeakMap
const _x = new WeakMap(a)class InnerClass {
constructor(x) {
_x.set(this, x)
}
getX() {
return x.get(this)}}return InnerClass
})()
let myClass = new MyClass(5)
console.log(myClass.getX()) / / 5
console.log(myClass.x) // undefined
Copy the code
9. Array sort
The bubbling
function bubbleSort(arr) {
let len = arr.length
for (let i=len; i>=2; i--) { // The first row is automatically the smallest
for (let j=0; j<i-1; j++) { // Gradually narrow down the scope
if (arr[j] > arr[j+1])
[arr[j], arr[j+1]] = [arr[j+1], arr[j]]
}
}
return arr
}
Copy the code
Selection sort
Implementation idea: iterate over the element after itself, the smallest element with their position.
function selectSort(arr) {
let len = arr.length
for (let i=0; i<len-1; i++) {
for (let j=i; j<len; j++) {
if (arr[j] < arr[i])
[arr[i], arr[j]] = [arr[j], arr[i]]
}
}
return arr
}
Copy the code
Insertion sort
Insert elements into a sorted array.
function insertSort(arr) {
for (let i=1; i<arr.length; i++) { // arr[0] defaults to sorted arrays
for (let j=i; j>0; j--) {
if (arr[j] < arr[j-1]) {
[arr[j],arr[j-1]] = [arr[j-1],arr[j]]
} else { break}}}return arr
}
Copy the code
Quick sort
Implementation idea: select the base value mid, loop the original array, less than the base value put the left array, greater than the right array, and then concat combination, finally rely on recursion to complete sorting.
function quickSort(arr) {
if (arr.length <= 1) return arr
let left = [], right = [], mid = arr.splice(0.1)
for (let i=0; i<arr.length; i++) {
if (arr[i] < mid) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat(mid, quickSort(right)) // Don't forget mid
}
Copy the code
sort
Here is just the idea of sort implementation, the SORT method in V8 engine is a relatively advanced algorithm compared to other methods.
Here’s a chart:
Sorting algorithm | Average time complexity | Spatial complexity | Best case scenario! | The stability of |
---|---|---|---|---|
Bubble sort | O(n^2) | O(1) | O(n) | stable |
Selection sort | O(n^2) | O(1) | O(n^2) | unstable |
Insertion sort | O(n^2) | O(1) | O(n) | stable |
Quick sort | O(nlogn) | O(logn) | O(nlogn) | unstable |
Stability definition: if the relative positions of two equal numbers before and after sorting remain unchanged, the algorithm is stable. The advantage is that the results of the first key sort can be used for the second key sort.
Let the number of elements to be sorted be n:
- when
n <= 10
, insert sort is used(When n is small enough, insert sort is faster than quicksort, see table). - when
n > 10
, three-way quicksort is used.
Where 10 < n <= 1000, the median is used as the sentry element; N > 1000, pick one element every 200 to 215, put it in a new array, and sort it to find the middle number as the median.
10. Array deduplication
Double loop
function unique(arr) {
for (let i=0; i<arr.length; i++) { // Note that the length of arr varies
for (let j=i+1; j<arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1)
j--
}
}
}
return arr
}
Copy the code
indexOf
function unique(arr) {
let res = []
for (let i=0; i<arr.length; i++) {
let current = arr[i]
if (res.indexOf(current) === -1) res.push(current)
}
return res
}
// Alternatively, use filter + indexOf
function unique(arr) {
let res = arr.filter(function(item, index, array){
return arr.indexOf(item) === index;
})
return res;
}
Copy the code
Sort and de-weight
function unique(arr) {
let res = []
let sortedArray = arr.concat().sort()
let lastVal
for (let i=0; i<sortedArray.length; i++) {
// If the first element or adjacent elements are different
if(! i || lastVal ! == sortedArray[i]) res.push(sortedArray[i]) lastVal = sortedArray[i] }return res
}
// Alternatively, use sort + filter
function unique(arr) {
return arr.concat().sort().filter(function(item, index, array){
return! index || item ! == arr[index -1]})}Copy the code
ES6 Set or Map
function unique(arr) {
return [...new Set(arr)];
}
// Alternatively, use Map
function unique (arr) {
const last = new Map(a)return arr.filter((item) = >! last.has(item) && last.set(item,1))}Copy the code
11.map
According to the MAP specification to simulate the implementation:
Array.prototype.map = function(callbackFn, thisArg) {
if (this= = =null || this= = =undefined)
throw new TypeError(`Cannot read property 'map' of The ${this}`)
// Handle callback type exceptions
if (Object.prototype.toString.call(callbackFn) ! = ='[object Function]')
throw new TypeError(`${callbackFn} is not a function`)
let O = Object(this), // specify that this be converted to an object first
T = thisArg,
len = O.length >>> 0.// Ensure len is a number and an integer
A = new Array(len)
for (let k=0; k<len; k++) {
if (k in O) { // The prototype chain looks for attributes
let mappedValue = callbackFn.call(T, O[k], k, O)
A[k] = mappedValue
}
}
return A
}
Copy the code
12.reduce
Simulation implementation according to reduce specification:
Array.prototype.reduce = function(callbackFn, initialValue) {
if (this= = =null || this= = =undefined)
throw new TypeError(`Cannot read property 'reduce' of The ${this}`)
// Handle callback type exceptions
if (Object.prototype.toString.call(callbackFn) ! = ='[object Function]')
throw new TypeError(`${callbackFn} is not a function`)
let O = Object(this), // specify that this be converted to an object first
k = 0,
len = O.length >>> 0.// Ensure len is a number and an integer
accumulator = initialValue
if (accumulator === undefined) {
for (; k<len; k++) {
if (k in O) {
accumulator = O[k]
k++
break}}}if (k === len && accumulator === undefined) // Indicates that the array is empty
throw new Error('Each element of the array is empty')
for(; k<len; k++) {
if (k in O) {
accumulator = callbackfn.call(undefined, accumulator, O[k], k, O); }}return accumulator
}
Copy the code
13.filter
According to the Filter specification to simulate the implementation:
Array.prototype.filter = function(callbackFn, thisArg) {
if (this= = =null || this= = =undefined)
throw new TypeError(`Cannot read property 'filter' of The ${this}`)
// Handle callback type exceptions
if (Object.prototype.toString.call(callbackFn) ! = ='[object Function]')
throw new TypeError(`${callbackFn} is not a function`)
let O = Object(this), // specify that this be converted to an object first
resLen = 0,
len = O.length >>> 0.// Ensure len is a number and an integer
res = []
for (let i=0; i<len; i++) {
if (i in O) { // The prototype chain looks for attributes
let element = O[i];
if (callbackfn.call(thisArg, O[i], i, O)) res[resLen++] = element
}
}
return res
}
Copy the code
14. Random strings
function generateRandomString(len) {
let randomStr = ' '
for (; randomStr.length<len; randomStr+=Math.random().toString(36).substr(2)) {}
return randomStr.substr(0, len)
}
Copy the code
15. Fibonacci numbers
Are you hungry after all this studying? Congratulations on finding…
Take a look at the Fibonacci sequence created by the creator of up, and, boy, tears are trickled out of his mouth…
// The time complexity is O(2^n)
function fibSequence(n) {
if (n === 1 || n === 2) return n - 1;
return fib(n - 1) + fib(n - 2)}// Or use iteration, O(n), recommended!
function fibSequence(n) {
let a = 0, b = 1, c = a + b
for (let i=3; i<n; i++) {
a = b
b = c
c = a + b
}
return c
}
Copy the code
16. Shallow copy
Shallow copy:
- Only one layer of objects can be copied. If there are nested objects, shallow copy cannot be used.
- Potential problem: If the copied property is a reference type, the copied memory address will affect each other.
const shallowClone = (target) = > {
if (typeof target === 'object'&& target ! = =null) {
const cloneTarget = Array.isArray(target) ? [] : {}for (let prop in target) {
if (target.hasOwnProperty(prop)) { // Whether it is its own (non-inherited) attribute
cloneTarget[prop] = target[prop] // Consider only one layer of objects}}return cloneTarget
} else {
return target // Base type returns directly}}// Or you can
console.log(Object.assign(array, ... sources))console.log(array.concat())
console.log(array.slice())
console.log([...array])
Copy the code
17. A deep copy
A deep copy creates a copy of an object with different reference addresses. Deep copy is a good choice when you want to use an object but don’t want to modify the original object.
JSON.parse(JSON.stringify());
Copy the code
The beggar version above already covers most application scenarios, but forget it when interviewing! It has several problems:
- Unable to resolve
A circular reference
. - Unable to copy special objects such as
Function, RegExp, Date, Set, Map
And so on.
If we encounter complex objects in our work, we can use a library of tools, such as Lodash’s cloneDeep method. Don’t abuse it!
Start with a simple comb:
Now, let’s implement a deep copy that covers most scenarios (you can skip this and see the next version for easier understanding) :
// Map is strongly referenced, and attributes need to be cleared manually to free memory.
// WeakMap weak reference, may be garbage collection at any time, so that memory timely release, is the only choice to solve circular reference.
function cloneDeep(obj, map = new WeakMap(a)) {
if (obj === null || obj === undefined) return obj // Do not copy
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
// Base types do not require deep copies
if (typeofobj ! = ='object' && typeofobj ! = ='function') return obj
// Handle normal functions and arrow functions
if (typeof obj === 'function') return handleFunc(obj)
// Make a deep copy of an object
if (map.get(obj)) return map.get(obj)
let cloneObj = new obj.constructor()
// Find the constructor from the parent class stereotype, which points to the current class itself.
map.set(obj, cloneObj)
if (getType(obj) === '[object Map]') {
obj.forEach((item, key) = >{ cloneObj.set(cloneDeep(key, map), cloneDeep(item, map)); })}if (getType(obj) === '[object Set]') {
obj.forEach(item= >{ cloneObj.add(cloneDeep(item, map)); })}for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], map)
}
}
return cloneObj
}
// Get a more detailed data type
function getType(obj) {
return Object.prototype.toString.call(obj)
}
// Handle normal functions and arrow functions
function handleFunc(func) {
if(! func.prototype)return func // The arrow function returns itself directly
const bodyReg = / (? <={)(.|\n)+(? =})/m
const paramReg = / (? < = \ () + (? =\)\s+{)/
const funcString = func.toString()
// Match function parameters and function body respectively
const param = paramReg.exec(funcString)
const body = bodyReg.exec(funcString)
if(! body)return null
if (param) {
const paramArr = param[0].split(', ')
return new Function(... paramArr, body[0])}else {
return new Function(body[0])}}Copy the code
The following version I changed:
function cloneDeep(obj, map = new WeakMap(a)) {
if(! (objinstanceof Object)) return obj; // Basic data
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags);
if (map.get(obj)) return map.get(obj); // Resolve circular references
if (obj instanceof Function) { // Solve the function
return function () {
return obj.apply(this, [...arguments]);
};
}
const res = new obj.constructor(); // The following is the array/ordinary object /Set/Map processing
obj instanceof Object && map.set(obj, res);
if (obj instanceof Map) {
obj.forEach((item, index) = > {
res.set(cloneDeep(index, map), cloneDeep(item, map));
});
}
if (obj instanceof Set) {
obj.forEach((item) = > {
res.add(cloneDeep(item, map));
});
}
Object.keys(obj).forEach((key) = > {
if (obj[key] instanceof Object) {
res[key] = cloneDeep(obj[key], map);
} else{ res[key] = obj[key]; }});return res;
}
const map = new Map(a); map.set({a: 1 }, "1");
const source = {
name: "Jack".meta: {
age: 12.birth: new Date("1997-10-10"),
ary: [1.2, { a: 1}].say() {
console.log("Hello");
},
map
},
};
source.source = source;
const newObj = cloneDeep(source);
console.log(newObj.meta.ary[2] === source.meta.ary[2]); // false
console.log(newObj.meta.birth === source.meta.birth); // false
console.log(newObj);
Copy the code
18. Parse the URL
What does the full URL look like first?
🌰 : https://keith:[email protected]:80/file? Test =3& Miao =4#heading-0
Simple implementation with re:
function parseUrl(url) {
/ / scheme: / / user: passwd @ part
let schemeStr = '(? : [[# ^ /?] +))? / / (? (*) [^ :] (? : :? (. *)) @)? '.// host:port path? Part of the query
urlStr = '(? : [[# ^ /? :] *) :? ([0-9] +)? ? ([^? #] *) (\ \? (? : [^ #] *))? './ / # fragments
fragmentStr = '(# (? : *)) '
let pattern = RegExp(` ^${schemeStr}${urlStr}${fragmentStr}? `)
let matched = url.match(pattern) || []
return {
protocol: matched[1]./ / agreement
username: matched[2]./ / user name
password: matched[3]./ / password
hostname: matched[4]./ / host
port: matched[5]./ / port
pathname: matched[6]./ / path
search: matched[7].// queryString queryString
hash: matched[8]./ / the anchor}}// Or you can
function parseUrl(url) {
const urlObj = new URL(url)
return {
protocol: urlObj.protocol,
username: urlObj.username,
password: urlObj.password,
hostname: urlObj.hostname,
port: urlObj.port,
pathname: urlObj.pathname,
search: urlObj.search,
hash: urlObj.hash
}
}
Copy the code
Parse the queryString queryString separately:
function parseQueryString(query) {
if(! query)return {}
query = query.replace(/ ^ \? /.' ')
const queryArr = query.split('&')
const result = {}
queryArr.forEach(query= > {
let [key, value] = query.split('=')
try {
key = decodeURLComponent(key || ' ').replace(/\+/g.' ')
value = decodeURLComponent(value || ' ').replace(/\+/g.' ')}catch(e) {
return console.log(e) // Invalid characters are not processed
}
const type = getQueryType(key)
switch(type) {
case 'ARRAY':
key = key.replace($/ / \ [\].' ') // Parse an array of the form 'list[]'
if(! result[key]) { result[key] = [value] }else {
result[key].push(value)
}
break;
case 'JSON':
key = key.replace(/ \ {\} $/.' ') // Objects of the form obj{} are resolved as objects
value = JSON.parse(value)
result.json = value
break;
default:
result[key] = value
}
})
return result
}
function getQueryType (key) {
if (key.endsWith('[]')) return 'ARRAY'
if (key.endsWith('{}')) return 'JSON'
return 'DEFAULT'
}
// Or you can, if you are prepared to be hit by an interviewer...
/ / simple version
function getUrlQuery(search) {
let searchObj = {};
for (let [key, value] of new URLSearchParams(search)) {
searchObj[key] = value
}
return searchObj
}
Copy the code
Of course, this is not rigorous enough to take into account the following issues:
- How to handle the same field
- There is no replacement
+
为% 20
- only
key
/ onlyvalue
- .
In our work, we recommend two open source libraries: JS-URL and Query-String. The boundaries are taken into account, and of course they are more complex than our implementation.
And don’t forget nodeJS’s two module methods: url.parse and queryString.parse are already pretty handy.
19.JSONP
JSONP: common cross-domain means, using the
const jsonp = ({url, params, callbackName}) = > {
const generateURL = () = > { // Generate an address based on the URL format
let dataStr = ' '
for (let key in params) {
dataStr += `${key}=${params[key]}& `
}
dataStr += `callback=${callbackName}`
return `${url}?${dataStr}`
}
return new Promise((resolve, reject) = > {
callbackName = callbackName || Math.random().toString()
let scriptEle = document.createElement('script')
scriptEle.src = generateURL()
document.body.appendChild(scriptEle)
// The server returns the string '${callbackName}(${server data})', which the browser can parse.
window[callbackName] = (data) = > {
resolve(data)
document.body.removeChild(scriptEle) // Don't forget to clear the DOM}})}Copy the code
20. Anti-shake/throttling
Image stabilization
- QA: Why did I press this Button and send two requests in a row?
- FE: Because you pressed it twice in a row.
- QA: You set limits.
Ok, so the purpose of this is to avoid unnecessary actions, such as this button where QA wants to perform only one last click for a short period of time.
My usual way of memory also has a kind: the mage reads, the last one will plan the casting of the previous one…
const debounce = (fn, delay) = > {
let timer = null
return function(. args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() = > { // Here is the arrow function, this refers to the outer non-arrow function this
fn.apply(this, args)
}, delay)
}
}
Copy the code
The throttle
Throttling is a fixed frequency trigger, and is most appropriate for scroll events.
function throttle(fn, interval) {
let flag = true
return function(. args) {
if(! flag)return;
flag = false
setTimeout(() = > {
fn.apply(this, args)
flag = true
}, interval)
}
}
// Or you can choose what you like.
function throttle(fn, interval) {
let last = 0 // Execute directly for the first time
return function (. args) {
let now = +new Date(a)if(now - last < interval) return;
last = now // Update last as time runs out
fn.apply(this, args)
}
}
Copy the code
Enhanced throttling
Now the button is using the anti-shock version above…
- QA pressed the button twice in a row, only the last time the request was sent, the test passed, and the OK gesture was made.
- FE was relieved.
- QA came on again and began to point and point at Button without getting tired.
- QA: Why is it never triggered?
- FE:…
Enhanced version of unlimited anti – shake throttling, fixed frequency trigger.
const throttle = (fn, delay) = > {
let timer = null, last = 0
return function(. args) {
let now = +new Date(a)if (now - last < delay && timer) {
clearTimeout(timer)
timer = setTimeout(() = > {
fn.apply(this, args)
}, delay)
} else { // When the delay is reached, the delay is triggered.
last = now
fn.apply(this, args)
}
}
}
Copy the code
21. Lazy loading of images
Sometimes you go through a lot of hard work with several performance optimizations, and the result is not as good and cost-effective as you can get with compressed, lazy loading images. So performance optimization, not mindless optimization.
There are three ways to realize lazy image loading:
- ClientHeight, scrollTop, and offsetTop
- getBoundingClientRect
- IntersectionObserver
First, give all images a placeholder resource:
<img src="default.jpg" data-src="https://www.xxx.com/target-1.jpg" />
<img src="default.jpg" data-src="https://www.xxx.com/target-2.jpg" />.<img src="default.jpg" data-src="https://www.xxx.com/target-39.jpg" />
Copy the code
① clientHeight, scrollTop and offsetTop
First above:
You can see that the image’s offsetTop is less than the purple line (scrollHeight + clientHeight) will be displayed in the window.
let imgs = document.getElementsByTagName("img"), count = 0
// First load
lazyLoad()
// Check whether the image reaches the viewport by listening for scroll events
window.addEventListener('scroll', throttle(lazyLoad, 160))
function lazyLoad() {
let viewHeight = document.documentElement.clientHeight // Viewport height
// The height of the scroll bar
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
for(let i=count; i<imgs.length; i++) {
// The element now appears in the viewport
if(imgs[i].offsetTop < scrollTop + viewHeight) {
if(imgs[i].getAttribute("src")! = ="default.jpg") continue;
imgs[i].src = imgs[i].getAttribute("data-src")
count ++
}
}
}
Copy the code
(2) getBoundingClientRect
The dom element’s getBoundingClientRect().top attribute directly determines whether the image is present in the current viewport.
// Just modify the lazyLoad function
function lazyLoad() {
for(let i=count; i<imgs.length; i++) {
if(imgs[i].getBoundingClientRect().top < document.documentElement.clientHeight) {
if(imgs[i].getAttribute("src")! = ="default.jpg") continue;
imgs[i].src = imgs[i].getAttribute("data-src")
count ++
}
}
}
Copy the code
(3) IntersectionObserver
The built-in API of IntersectionObserver browser can monitor scroll events of window, judge whether it is in viewport, and perform throttling. This API needs to be polyfilled.
let imgs = document.getElementsByTagName("img")
const observer = new IntersectionObserver(changes= > {
for(let i=0, len=imgs.length; i<len; i++) {
let img = imgs[i]
// Use this property to determine whether it is in the viewport. Return Boolean
if(img.isIntersecting) {
const imgElement = img.target
imgElement.src = imgElement.getAttribute("data-src")
observer.unobserve(imgElement) // Remove the observation}}})Array.from(imgs).forEach(item= > observer.observe(item)) / / call
Copy the code
22. Asynchronous callback to the Promise series
Promise
Promise to achieve combing:
- Chain calls
- Error capture (bubbling)
const PENDING = 'PENDING'; / /
const FULFILLED = 'FULFILLED'; / / has been successful
const REJECTED = 'REJECTED'; / / has failed
class Promise {
constructor(exector) {
// Initialization state
this.status = PENDING;
// Place success and failure results on this for easy access to then and catch
this.value = undefined;
this.reason = undefined;
// Success callback function queue
this.onFulfilledCallbacks = [];
// Failed state callback function queue
this.onRejectedCallbacks = [];
const resolve = value= > {
// You can change a state only if it is in progress
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// The success state functions are executed in sequence
this.onFulfilledCallbacks.forEach(fn= > fn(this.value)); }}const reject = reason= > {
// You can change a state only if it is in progress
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// The failed state functions are executed in sequence
this.onRejectedCallbacks.forEach(fn= > fn(this.reason))
}
}
try {
Execute executor immediately
// Pass internal resolve and reject to the executor. The user can call resolve and reject
exector(resolve, reject);
} catch(e) {
// The executor executes an error, throwing out the error content rejectreject(e); }}then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value= > value;
onRejected = typeof onRejected === 'function'? onRejected:
reason= > { throw new Error(reason instanceof Error ? reason.message:reason) }
/ / save this
const self = this;
return new Promise((resolve, reject) = > {
if (self.status === PENDING) {
self.onFulfilledCallbacks.push(() = > {
// try to catch an error
try {
// Simulate microtasks
setTimeout(() = > {
const result = onFulfilled(self.value);
// There are two kinds of cases:
// 1. The return value of the callback is Promise
// 2. If it is not a Promise, call the new Promise's resolve function
result instanceof Promise ? result.then(resolve, reject) :
resolve(result)
})
} catch(e) { reject(e); }}); self.onRejectedCallbacks.push(() = > {
// Do the following
try {
setTimeout(() = > {
const result = onRejected(self.reason);
// Reject: reject
result instanceof Promise ? result.then(resolve, reject) :
reject(result)
})
} catch(e) { reject(e); }})}else if (self.status === FULFILLED) {
try {
setTimeout(() = > {
const result = onFulfilled(self.value)
result instanceof Promise ? result.then(resolve, reject) : resolve(result)
});
} catch(e) { reject(e); }}else if (self.status === REJECTED){
try {
setTimeout(() = > {
const result = onRejected(self.reason);
result instanceof Promise ? result.then(resolve, reject) : reject(result)
})
} catch(e) {
reject(e)
}
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
static resolve(value) {
if (value instanceof Promise) {
// If it is a Promise instance, return it directly
return value;
} else {
// If it is not a Promise instance, return a new Promise object, which is FULFILLED
return new Promise((resolve, reject) = >resolve(value)); }}static reject(reason) {
return new Promise((resolve, reject) = >{ reject(reason); }}})Promise.prototype.finally = function(callback) {
this.then(value= > {
return Promise.resolve(callback()).then(() = > {
return value
})
}, error= > {
return Promise.resolve(callback()).then(() = > {
throw error
})
})
}
Copy the code
Promise.resolve
Resolve static method:
- Pass the parameter as a Promise and return it directly.
- The parameter is passed as a Thenable object, and the returned Promise follows the object, taking its final state as its own.
- Otherwise, a Promise object with that value as a success status is returned directly.
Promise.resolve = (param) = > {
if(param instanceof Promise) return param / / in 1
return new Promise((resolve, reject) = > {
if (param && param.then && typeof param.then === 'function') { / / in 2
// A successful param state calls resolve, which changes the state of the new Promise to successful and vice versa
param.then(resolve, reject)
} else { / / in accordance with 3
resolve(param)
}
})
}
Copy the code
Promise.reject
// bubble capture
Promise.reject = function (reason) {
return new Promise((resolve, reject) = > {
reject(reason)
})
}
Copy the code
Promise.all
Promise. All
- Resolve is passed as an empty iterable.
- If one of the arguments fails, the promise object returned by promise.all fails.
- In any case, promise.all returns an array as the result of the completion state of the Promise.
Promise.all = function(promises) {
return new Promise((resolve, reject) = > {
let result = [],
index = 0,
len = promises.length
if(len === 0) {
resolve(result)
return;
}
for(let i=0; i<len; i++) {
// Why not just promise[I]. Then, consider that promise[I] might not be a promise object
Promise.resolve(promise[i]).then(data= > {
result[i] = data
index++
if(index === len) resolve(result)
}).catch(err= > {
reject(err)
})
}
})
}
Copy the code
Promise.race
Promise. Race Resolve and stop execution once a Promise is completed.
Promise.race = function(promises) {
return new Promise((resolve, reject) = > {
let len = promises.length
if(len === 0) return;
for(let i=0; i<len; i++) {
// Promises [I] are not promises
Promise.resolve(promises[i]).then(data= > {
resolve(data)
return;
}).catch(err= > {
reject(err)
return; })}})}Copy the code
❤️ look here (* ̄)  ̄)
If you find this post inspiring, please like it and make it available to more people. This is really important to me.
Previous selections:
“Intermediate and Advanced Front-end Interview” Handwritten code Collection (2)
Byte interview -TLS handshake process details
reference
The secret to JavaScript handwritten code
JavaScript standard built-in objects
Original JS soul of the question series
How to write a deep copy that will impress your interviewer
When to encode space to plus (+) or %20