This article will first understand the usage of array APIs, and then simulate the implementation of these APIs. If you think there is anything wrong, please point out the following!
1. forEach method
This method will run the passed in function for each item of the array element without returning a value. This is equivalent to using a for loop to traverse the array. For example:
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; numbers.forEach((item, index, array) => { // Perform some actions item += 2 }) console.log(numbers);
We found that the array elements were not changed
You can use the forEach method instead of the for loop to traverse the array
Let's take a look at the following code to summarize
let arr1 = [{ name: 'ljc', age: 19 }, { name: 'xy', age: 18 }] arr1.forEach(item => { item.age += 1 }) console.log(arr1);
From the above two codes, we can see that the age attribute value of both members is increased by 1
So we can simply draw a conclusion: when the elements in the array are value types, forEach will never change the array. When the elements in the array are reference types, the array can be changed
Note: because the forEach method does not return a value, forEach does not support chained operations
1-1 handwritten forEach method
The native forEach method receives two parameters callback and thisArg, and the callback function passes in three parameters: the value of the current item of the array, the index, and the array itself
Array.prototype.myForEach = function (callback, thisArg) { // Determine whether the element calling the API is null if (this == null) { throw new TypeError('this is null or not defined') } // Judge whether it is function if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function') } // Get the caller arr through this const arr = this // Determine loop variables let index = 0 // Loop traversal calls callback for each array element while (index < arr.length) { // Determine whether this item exists if (index in arr) { // Point this to thisArg through call, and pass in three parameters callback.call(thisArg, arr[index], index, arr) } index++ } }
2. map method
Compared with the forEach method, the map method has a return value, while the forEach method has no return value.
map is also called mapping, that is, mapping the original array to a new array
- Each element in the array will call a provided function and return the result.
- A new array will be created, and a bearing object is required, that is, a new object will be returned
- The original array will not change unless it is carried by the original array
usage method
let arr = [1, 2, 3, 4, 5] let newArr = arr.map(item => item * 2) console.log(newArr); // [2, 4, 6, 8, 10]
map needs to have a return value, which can be abbreviated by arrow function
Error prone point
Every element in the map must execute a callback function, so there must be a return. Therefore, the array cannot be filtered by map
You can see the gray undefined. Bye
2-2 handwritten map method
- First, exclude empty arrays and no callback functions
- According to the requirements of map, you need to create a new array, execute the function and return the array
Array.prototype.myMap = function (callback, thisArg) { // Like forEach, two exclusions are required if (this == undefined) { throw new TypeError('this is null or not defined'); } if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } // Unlike forEach, map returns a new array const ret = [] // Get function caller const arr = this // Array length let len = arr.length // Execute a callback function on each element for (let i = 0; i < len; i++) { // Check if i is in arr if(i in arr) { ret[i] = callback.call(thisArg, arr[i], i, arr) } } // Returns a processed array return ret }
3. filter
From the name of filter, you can know that it is used for filtering. Like map, it returns a new array of objects without changing the original array
usage method
Thus, we can filter out the elements whose array elements are less than 3
3-3 handwriting filter method
Compared with the map method, the filter needs to form a new array of elements that meet the conditions and return them
Array.prototype.myFilter = function(callback,thisArg) { if (this == undefined) { throw new TypeError('this is null or not defined'); } if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } // New array const res = [] // Save this const arr = this // Calculate array length in advance const len = arr.length for(let i = 0;i<len;i++) { if(i in arr) { // Judge whether the element has a return value after function call // To determine whether the screening rules are met, if(callback.call(thisArg,arr[i],i,arr)) { res.push(arr[i]) } } } // Finally, remember to return a new array return res }
4. some method
The some method is used to check whether there are qualified values in the array. The return value is a Boolean value
usage method
some method is more performance friendly, because it does not need to traverse all, as long as it finds a qualified one, it will 9 return true
According to this principle, we can write a some method
4-4 handwriting some method
Array.prototype.mySome = function (callback, thisArg) { if (this == undefined) { throw new TypeError('this is null or not defined'); } if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } let arr = this let len = arr.length for (let i = 0; i < len; i++) { if (i in arr) { if (callback.call(thisArg, arr[i], i, arr)) { return true } } } return false }
5. every method
Compared with some, each member returns true only if it meets the conditions, and false if it does not meet the conditions
true will be returned only if all conditions are met
5-5 handwriting every method
Array.prototype.myEvery = function (callback, thisArg) { if (this == undefined) { throw new TypeError('this is null or not defined'); } if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } const arr = this const len = arr.length for (let i = 0; i < len; i++) { if (i in arr) { if (!callback.call(thisArg, arr[i], i, arr)) { return false } } } return true }
6. find and findIndex methods
If a qualified element is found, the current element will be returned if it is found, and undefined if it is not found
The same shape as the find method is also the findIndex method, which returns the index value of the first element that meets the condition
find usage
Return satisfied elements
findIndex usage
Returns the index that meets the
6-6 handwritten find method
Loop through the array and call the passed in function. If the conditions are met, the array element corresponding to the current index will be returned, and only the first one will be returned
Array.prototype.myFind = function (callback, thisArg) { if (this == undefined) { throw new TypeError('this is null or not defined'); } if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } // Save this, that is, the caller const arr = this const len = arr.length for (let i = 0; i < len; i++) { if (i in arr) { if (callback.call(thisArg, arr[i], i, arr)) { return arr[i] } } } return undefined }
findIndex method
The difference from find is the return value. You only need to change return arr[i] to return i
Small scene
For the above 6 or 7 array methods, you will find the difference in implementation, that is, the lines of code are not easy to remember. Their use scenarios are unknown. Use a small scenario to show the use scenarios of these API s
Previously on: in a company, the boss is considering giving employees a promotion and a raise
Company employee data
let staff = [ {name: 'a', salary: 20000, age: 36}, {name: 'b', salary: 19000, age: 34}, {name: 'c', salary: 18000, age: 20}, {name: 'd', salary: 17000, age: 18} ]
🤵 Boss: "this year's performance is good, and the wages of all employees have increased by 1000"
👨🦲 Programmer brother: "it's simple. Just use forEach. The code and results are like this"
staff.forEach(item => item.salary += 1000)
🤵 Boss: "make me a salary form"
👨🦲 Programmer brother: "no problem. Map has a return value. You can use map"
w = staff.map(item => item.salary += 1000) console.log(w) // [21000, 20000, 19000, 18000]
🤵 Boss: "the company has been established for so many years. Please give me a list of employees over 33 years old."
👨🦲 Programmer brother: "OK, no problem, use filter"
w = staff.filter(item => item.age > 33)
👨🦲 Programmer brother: "employees a and B are over 33 years old"
🤵 Boss: "then help me see if there are employees under the age of 18"
👨🦲 Programmer: "OK, let's use some method. We don't have employees younger than 18 years old."
w = staff.some(item => item.age < 18) // false
🤵 Boss: "the company is now listed. Do you see if the wages of our employees are more than 1.6w?"
👨🦲 Programmer brother: "it's really good. It's all over 1.6w. What else do you need?"
w = staff.every(item => item.salary > 16000) // true
🤵 Boss: "then help me find someone over 35 years old. Just the first one."
👨🦲 Programmer's younger brother: "simply check the first one above 35, called a"
w = staff.find(item => item.age > 35) // {name: "a", salary: 20000, age: 36}
🤵 Boss: "how many places does it rank in the company's employee data?"
👨🦲 Programmer brother: "you're boring. It all depends."
w = staff.findIndex(item => item.age > 35) // 0
👨🦲 Programmer brother: "0, first, veteran level"
🤵 Boss: "very good. Your skills are good. Then calculate the total salary and ask the Secretary to give money to the finance department."
👨🦲 Programmer brother: "... Wait a minute, I'll learn reduce again"
7. reduce method
Different from the iterative method, reduce is a merging method. Merging does not execute the objective function for each item. It can be summarized into the following steps:
- Continuously take out the first two items of the array, execute the objective function on it, and calculate the return value
- Insert the return value into the header of the array, that is, as ayyay[0]
- This process continues until each item in the array is accessed once
- Return final result
Examples
const arr = [1, 2, 3] const res = arr.reduce((prev, cur) => prev + cur) console.log(res); // 6
In the above code, reduce does the following merging operations
[1, 2, 3] // Take out 1 and 2, perform 1 + 2 and fill in 3 [3, 3] // Take out 3 and fill in 6 [6] // Final return 6
7-7 handwritten reduce method
Write according to the 4-step rule above
Array.prototype.myReduce = function (callback, initialValue) { // Determine whether the element calling the API is null if (this == null) { throw new TypeError('this is null or not defined') } // Judge whether it is function if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function') } const arr = this const len = arr.length // Second parameter let accumulator = initialValue let index = 0 // If the second parameter is undefined, the first valid value of the array // As the initial value of the accumulator if (accumulator === undefined) { // The first valid value found in the array is not necessarily arr[0] while (index < len && !(index in arr)) { index++ } if (index >= len) { throw new TypeError('Reduce of empty array with no initial value') } // Output the first valid array element as the first element of the accumulator accumulator = arr[index++] } while (index < len) { if (index in arr) { // arr[index] is the next element of the accumulator accumulator = callback.call(undefined, accumulator, arr[index], index, arr) } // Continuous backward shift index++ } // Return results return accumulator }
7-x implementing map with reduce
I have seen this topic in many places
Implementation idea: take the elements traversed each time as the parameters of the incoming function, and store the function execution results into a new array for return
Core: the map function receives a function as a parameter, and the function as a parameter receives three parameter values, respectively traversing each element of the array, the element index and the array itself. These three parameters just correspond to the second, third and fourth parameters of the first function parameter received by the reduce function
Array.prototype.mapReduce = function (callback, context = null) { if (this == null) { throw new TypeError('this is null or not defined') } // Judge whether it is function if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function') } let arr = this return arr.reduce((pre, cur, index, array) => { let res = callback.call(context, cur, index, array) return [...pre, res] }) }
That's all for today's hand tearing code. If you have any questions, please add corrections in the comment area!