Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
preface
There are many higher-order functions in JavaScript, such as Map, filter, every, as well as ES6’s new find, which can greatly improve the efficiency of writing code after skilled use. Let’s take a look at these higher-order functions and implement them using native JS simulation.
So what is a higher-order function?
A function that satisfies at least one of the following conditions:
- Takes one or more functions as arguments
- Output a function
Most of the higher-order functions in JavaScript take a function as an argument, which is as follows:
Array.prototype.func = function(callback(currentValue[, index[, array]]){
}[, thisArg])
Copy the code
callback
: a callback function that operates on array elementscurrentValue
: The current element being processed- The index of the current element
- Call an array of higher-order functions
thisArg
: Optional, executecallback
Functionthis
forEach
usage
ForEach is mainly used for simple traversal of arrays. The basic usage is as follows
arr = [1.2.3.4]
arr.forEach((val, index) = > {
console.log(val, index)
})
// Equivalent to the original for loop
for (var i = 0; i<arr.length; i++) {
console.log(arr[i], i)
}
Copy the code
Analog implementation
Let’s recall what happened inside the forEach example above. It’s simply a for loop that runs the arr.length callback, which takes the corresponding element index, element value, and array itself. Then we can simulate the general running process.
for (var i = 0; i<arr.length; i++) {
callback(arr[i], i, arr)
}
Copy the code
Since forEach can also accept thisArg arguments as the context for callback functions, the above code is modified slightly with Call /apply.
callback.call(thisArg, arr[i], i, arr)
Copy the code
With the above analysis, we can write the complete simulation code:
Array.prototype.myForEach = function (callbackFn) {
// Check if this is valid
if (this= = =null || this= = =undefined) {
throw new TypeError("Cannot read property 'myForEach' of null");
}
// Check whether the callbackFn is valid
if (Object.prototype.toString.call(callbackFn) ! = ="[object Function]") {
throw new TypeError(callbackFn + ' is not a function')}// Get the array object that executes the method and the this object passed in
var _arr = this, thisArg = arguments[1] | |window;
for (var i = 0; i < _arr.length; i++) {
// Execute the callback functioncallbackFn.call(thisArg, _arr[i], i, _arr); }}Copy the code
map
usage
The map function performs a callback on each element of the array and returns a new array containing the result of the operation, basically as follows:
const users = [
{ name: 'John'.age: 34 },
{ name: 'Amy'.age: 20 },
{ name: 'camperCat'.age: 10}];// Request: Fetch the name of the users owner and store it in a new array
// Do not use map
names = []
for (var i = 0; i<users.length; i++){
names.push(users[i].name)
}
// Map operates on each element of the array, so the above code can be simplified using map
names = users.map(function (user) {
return user.name
})
// If you know the arrow function, you can simplify it further
names = user.map(user= > user.name)
Copy the code
Analog implementation
Given the forEach experience above, the map only needs to be modified so that the result returns a new array (exception judgments are omitted here).
Array.prototype.myMap = function(callbackFn) {
var _arr = this, thisArg = arguments[1] | |window, res = [];
for (var i = 0; i<_arr.length; i++) {
// Store the result of the operation
res.push(callbackFn.call(thisArg, _arr[i], i, _arr));
}
return res;
}
Copy the code
filter
usage
Filter, which performs a callback on each element of the array, returns the element whose callback result is true.
// Return an even number
arr = [1.2.3.4.5];
arr.filter(val= > val % 2= =0)
Copy the code
Analog implementation
Much like map’s implementation, map returns all elements after the callback, while filter returns only elements whose callback result is true.
Array.prototype.myFilter = function(callbackFn) {
var _arr = this, thisArg = arguments[1] | |window, res = [];
for (var i = 0; i<_arr.length; i++) {
// The callback function executes true
if(callbackFn.call(thisArg, _arr[i], i, _arr)) { res.push(_arr[i]); }}return res;
}
Copy the code
every
usage
Every does not return an array. It returns a Boolean true/false. Each element of the array executes a callback function that returns true if all results are true, and false otherwise.
arr = [1.3.5.7.8.9]
// false, 8 is an even number
arr.every(ele= > ele % 2= =1)
arr2 = [2.4.6]
// true is even
arr2.every(ele= > ele % 2= =0)
Copy the code
Analog implementation
Array.prototype.myEvery = function(callbackFn) {
var _arr = this, thisArg = arguments[1] | |window;
// The start identifier value is true
// Return false on a callback
// If the loop completes, it means that all callbacks return true and the final result is true
var flag = true;
for (var i = 0; i<_arr.length; i++) {
// If the callback is false, the function is interrupted
if(! callbackFn.call(thisArg, _arr[i], i, _arr)) {return false; }}return flag;
}
Copy the code
some
usage
Some, like every, returns a Boolean value. Some returns true as long as the result of the callback function has a true, and false otherwise.
Analog implementation
Array.prototype.mySome = function(callbackFn) {
var _arr = this, thisArg = arguments[1] | |window;
// Start identifier value is false
// If a callback returns true, return true
// If the loop completes, it means that all callbacks return false and the final result is false
var flag = false;
for (var i = 0; i<_arr.length; i++) {
// If the callback is false, the function is interrupted
if (callbackFn.call(thisArg, _arr[i], i, _arr)) {
return true; }}return flag;
}
Copy the code
find/findIndex
usage
Find and findIndex are new array methods in ES6 that return the first array element/index that satisfies the callback function. When there is no element in the array that satisfies the callback function, undefined and -1 are returned, respectively.
const users = [
{ name: 'John'.age: 34 },
{ name: 'Amy'.age: 20 },
{ name: 'camperCat'.age: 10}];Copy the code
-
Return the age of John whose name is
If there is no find method, we need to iterate over the name=Jonn and find the age. Using Find, however, can be done quickly and easily.
JohnAge = users.find(user= > user.name === 'John').age Copy the code
-
Returns the index whose name is Amy
In ES6, Array provided methods to find elements in an Array: indexOf, lastIndexOf, but neither of these methods can find objects.
// Return -1, indicating that Amy is not found users.indexOf({ name: 'Amy'.age: 20 }) // return hi = 1 users.findIndex(user= > user.name === 'Amy') Copy the code
IndexOf can also be used to find out if an element is present in an array, but it is not semantically good and needs to be compared with -1 every time, so ES6 added a new includes method.
Analog implementation
Find /findIndex returns the first element that satisfies the callback function. Some is similar, so their native code is similar.
Array.prototype.myFind = function(callbackFn) {
var _arr = this, thisArg = arguments[1] | |window;
// Returns true on a callback, directly returning the array element
// If the loop completes, it means that all callbacks return false and the final result is undefined
for (var i = 0; i<_arr.length; i++) {
// If the callback is false, the function is interrupted
if (callbackFn.call(thisArg, _arr[i], i, _arr)) {
return_arr[i]; }}return undefined;
}
Copy the code
reduce
usage
Reduce is slightly different from the previous approach:
arr.reduce(callback(accumulator, currentValue[, index[, array]]){
}[, initialValue])
Copy the code
callback
: a callback function that operates on array elementsaccumulator
: The return value of the accumulator’s cumulative callback; It is the cumulative value returned when the callback was last called, orinitialValue
currentValue
: The current element being processed- The index of the current element
- Call an array of higher-order functions
initialValue
: as the initial value for the first call to the function. If no initial value is provided, the first element in the array is used.
An error is reported when calling reduce with an empty array with no initial value
But the cumulative effect adds a lot of excitement to reduce and leads to a lot of useful uses.
- Array summation
arr = [0.1.2.3.4]
arr.reduce((accu, current) = > accu + current, 0)
Copy the code
- Summing up an array of objects
If you just use Reduce as above, the end result will be problematic
objArr = [{x: 1}, {x:2}, {x:3}]
objArr.reduce((accu, current) = > accu.x + current.x, 0)
Copy the code
The above code returns a NaN. Why is that?
Accumulator is an accumulator or an initial value since the last call. Therefore, after the first call, 3 is assigned to Accumulator. It no longer has an X attribute, so NaN is returned
// Method 1: Use map to extract values
objArr.map(obj= > obj.x).((accu, current) = > accu + current, 0)
// Method 2: give the initial value, each time run accu + current
objArr.reduce((accu, current) = > accu + current.x, 0)
Copy the code
- Count the number of occurrences of each element in the array
var names = ['Alice'.'Bob'.'Tiff'.'Bruce'.'Alice'];
var countedNames = names.reduce(function (allNames, name) {
if (name in allNames) {
allNames[name]++;
}
else {
allNames[name] = 1;
}
return allNames;
}, {});
Copy the code
Analog implementation
Array.prototype.myReduce = function(callbackFn) {
var _arr = this, accumulator = arguments[1];
var i = 0;
// Determine whether an initial value is passed in
if (accumulator === undefined) {
// An error is reported when calling reduce with an empty array with no initial value
if (_arr.length === 0) {
throw new Error('initVal and Array.length>0 need one')}// Assign the initial value to the first element of the array
accumulator = _arr[i];
i++;
}
for (; i<_arr.length; i++) {
// The computed result is assigned to the initial value
accumulator = callbackFn(accumulator, _arr[i], i, _arr)
}
return accumulator;
}
Copy the code
Past wonderful articles
- Cow guest latest front-end JS written test 100 questions
- A thorough understanding of prototypes and prototype chains in JavaScript
- JavaScript precompilation learning
- Complete understanding of EventLoop in JavaScript
- “2W word big chapter 38 interview questions” completely clear JS this pointing to the problem