Front-end common tool library and high-frequency interview questions (continuously updated..., welcome to star collection)
Source code address: https://github.com/niexq/coding-interview-questions, thank you for reading, welcome Star
to not get lost๐
๐ฅ coding-interview-questions
Common front-end tool library and high-frequency interview questions
Table of contents
- 1ใโ Lodash function list
- 2ใ๐ป Ramda function list
- 3ใ๐ป JavaScript Advanced Programming 4th Edition Study Notes
- 4ใ๐ป React interview questions
Lodash
Lodash is a JavaScript tool library
Lodash function list
Array
chunk
Split an array into multiple smaller arrays
const chunk = (arr, size) =>
Array.from({ length: Math.ceil(arr.length / size) }, (v, i) =>
arr.slice(i * size, i * size + size)
);
chunk(['a', 'b', 'c', 'd'], 2);
// => [['a', 'b'], ['c', 'd']]
chunk(['a', 'b', 'c', 'd'], 3);
// => [['a', 'b', 'c'], ['d']]
Idea: Cut the array into multiple small arrays according to the specified size. Use the Array.from() method of ES6 to create a new array with a length equal to the number of arrays after cutting. Use the slice() method to cut the original array according to the size after cutting. Divide by size and return each small array stored in a new array.
compact
Remove false values (false, null, 0, "", undefined, NaN) from the array
const compact = arr => arr.filter(Boolean);
compact([0, 1, false, 2, '', 3]);
// => [1, 2, 3]
Idea: Use the filter() method to filter out the true values in the array. The Boolean() function converts all false values into false and all true values into true.
concat
Merge multiple arrays
const concat = (...args) => [].concat(...args);
const array = [1];
const other = concat(array, 2, [3], [[4]]);
console.log(other);
// => [1, 2, 3, [4]]
console.log(array);
// => [1]
Idea: Use the ES6 spread operator (...) to convert the incoming parameters into an array, and then use the concat() method to combine all arrays into a new array and return it.
difference
Returns an array containing elements that are in the first array but not in the other arrays
// // The first implementation
// const difference = (arr, ...args) => arr.filter((item) => !args.flat().includes(item));
// 2nd implementation
const difference = (arr, ...args) =>
arr.filter(item => args.every(arg => !arg.includes(item)));
difference([3, 2, 1], [4, 2]);
// => [3, 1]
Idea: Use the filter() method to traverse the first array, use the every() method to traverse other arrays, filter out the elements in the first array that are not included in other arrays, and return a new array.
differenceBy
Similar to difference, but you can specify a function to compare the elements in the array
const differenceBy = (array, values, iteratee) => {
const fn = typeof iteratee === 'function' ? iteratee : item => item[iteratee];
const valuesSet = new Set(values.map(fn));
return array.filter(item => !valuesSet.has(fn(item)));
};
differenceBy([3.1, 2.2, 1.3], [4.4, 2.5], Math.floor);
// => [3.1, 1.3]
// The `property` iteratee shorthand.
differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], 'x');
// => [{ 'x': 2 }]
Idea: First determine whether the third parameter is a function. If so, use the function to compare the elements in the array for differential filtering. Otherwise, directly use lodash's differential filtering function to implement it.
differenceWith
Filter out elements from the first array that are not in the second array, using a custom comparison function to compare.
const differenceWith = (array, values, comparator) =>
array.filter(item => !values.some(value => comparator(item, value)));
const objects = [
{ x: 1, y: 2 },
{ x: 2, y: 1 },
];
_.differenceWith(objects, [{ x: 1, y: 2 }], _.isEqual);
// => [{ 'x': 2, 'y': 1 }]
Idea: Use the higher-order functions filter and some to compare two arrays and return the elements in the first array that are not included in the second array.
drop
Returns a new array, removing the first n elements from the original array
const drop = (arr, n = 1) => arr.slice(n);
drop([1, 2, 3]);
// => [2, 3]
drop([1, 2, 3], 2);
// => [3]
drop([1, 2, 3], 5);
// => []
drop([1, 2, 3], 0);
// => [1, 2, 3]
Idea: Use the slice() method to delete the first n elements in the original array and return them.
dropRight
Returns a new array, removing the last n elements from the original array
const dropRight = (arr, n = 1) =>
n >= arr.length ? [] : arr.slice(0, arr.length - n);
dropRight([1, 2, 3]);
// => [1, 2]
dropRight([1, 2, 3], 2);
// => [1]
dropRight([1, 2, 3], 5);
// => []
dropRight([1, 2, 3], 0);
// => [1, 2, 3]
Idea: According to the value of n, obtain a new array through the slice method of the array, thereby achieving the operation of deleting the last element.
dropRightWhile
Returns a new array, removing the elements from the last qualifying element to the end of the original array
const dropRightWhile = (array, iteratee) => {
let right = array.length - 1;
if (typeof iteratee === 'function') {
while (iteratee(array[right])) {
right--;
}
}
if (typeof iteratee === 'object' && !Array.isArray(iteratee)) {
const entries = Object.entries(iteratee);
while (entries.every(([key, value]) => array[right][key] === value)) {
right--;
}
}
if (Array.isArray(iteratee) && iteratee.length === 2) {
const [key, value] = iteratee;
while (array[right][key] === value) {
right--;
}
}
return array.slice(0, right + 1);
};
const users = [
{ user: 'barney', active: true },
{ user: 'fred', active: false },
{ user: 'pebbles', active: false },
];
dropRightWhile(users, o => !o.active);
// => objects for ['barney']
// The `matches` iteratee shorthand.
dropRightWhile(users, { user: 'pebbles', active: false });
// => objects for ['barney', 'fred']
// The `matchesProperty` iteratee shorthand.
dropRightWhile(users, ['active', false]);
// => objects for ['barney']
// The `property` iteratee shorthand.
dropRightWhile(users, 'active');
// => objects for ['barney', 'fred', 'pebbles']
Idea: This function implements a function that starts traversing from the end of the array, and when the element meets the passed iterator condition, deletes the element from the array and returns the new array after deletion. Iterators can be functions, objects, or arrays.
fill
Fill an array with specified values
const fill = (arr, value, start = 0, end = arr.length) =>
arr.fill(value, start, end);
const array = [1, 2, 3];
fill(array, 'a');
console.log(array);
// => ['a', 'a', 'a']
fill(Array(3), 2);
// => [2, 2, 2]
fill([4, 6, 8, 10], '*', 1, 3);
// => [4, '*', '*', 10]
Idea: Use the fill() method to replace the elements from the specified position to the specified position of the array with the specified value, and return the modified array.
findIndex
Returns the index of the first element that meets the criteria
const findIndex = (arr, fn) => arr.findIndex(fn);
const users = [
{ user: 'barney', active: false },
{ user: 'fred', active: false },
{ user: 'pebbles', active: true },
];
findIndex(users, o => o.user === 'barney');
// => 0
// The `matches` iteratee shorthand.
findIndex(users, { user: 'fred', active: false });
// => 1
// The `matchesProperty` iteratee shorthand.
findIndex(users, ['active', false]);
// => 0
// The `property` iteratee shorthand.
findIndex(users, 'active');
// => 2
Idea: Use the findIndex() method to find the subscript of the element that meets the conditions in the array, and return -1 if not found.
findLastIndex
Returns the index of the last element that meets the criteria
const findLastIndex = (arr, predicate) => {
if (typeof predicate === 'function') {
for (let i = arr.length - 1; i >= 0; i--) {
if (predicate(arr[i], i, arr)) {
return i;
}
}
} else if (Array.isArray(predicate)) {
const [key, value] = predicate;
for (let i = arr.length - 1; i >= 0; i--) {
if (arr[i][key] === value) {
return i;
}
}
} else if (typeof predicate === 'object') {
for (let i = arr.length - 1; i >= 0; i--) {
const keys = Object.keys(predicate);
const match = keys.every(key => predicate[key] === arr[i][key]);
if (match) {
return i;
}
}
} else {
for (let i = arr.length - 1; i >= 0; i--) {
if (arr[i] && arr[i][predicate]) {
return i;
}
}
}
return -1;
};
const users = [
{ user: 'barney', active: true },
{ user: 'fred', active: false },
{ user: 'pebbles', active: false },
];
findLastIndex(users, o => o.user === 'pebbles');
// => 2
// The `matches` iteratee shorthand.
findLastIndex(users, { user: 'barney', active: true });
// => 0
// The `matchesProperty` iteratee shorthand.
findLastIndex(users, ['active', false]);
// => 2
// The `property` iteratee shorthand.
findLastIndex(users, 'active');
// => 0
Idea: Use the findLastIndex() method to return the index of the last element in the array that meets the provided test function conditions. If the corresponding element is not found, -1 is returned.
head
Returns the first element in the array
const head = arr => arr[0];
head([1, 2, 3]);
// => 1
head([]);
// => undefined
Idea: Directly return the first element of the array.
flatten
Convert multidimensional array to one-dimensional array
const flatten = arr => [].concat(...arr);
flatten([1, [2, [3, [4]], 5]]);
// => [1, 2, [3, [4]], 5]
Idea: Use the spread operator to expand a multi-dimensional array, and then use the concat method to splice the expanded one-dimensional arrays together to obtain the final one-dimensional array.
flattenDeep
Convert a multi-dimensional array into a one-dimensional array recursively
const flattenDeep = arr =>
[].concat(...arr.map(v => (Array.isArray(v) ? flattenDeep(v) : v)));
flattenDeep([1, [2, [3, [4]], 5]]);
// => [1, 2, 3, 4, 5]
Idea: Use Array.map to traverse the array. For each element in the array, if it is an array, the flattenDeep function is called recursively, otherwise the element is returned directly. Finally, the spread operator is used to expand the array, and the concat method is used to splice it together.
fromPairs
Convert a two-dimensional array to an object
const fromPairs = arr =>
arr.reduce((obj, [key, val]) => ({ ...obj, [key]: val }), {});
fromPairs([
['a', 1],
['b', 2],
]);
// => { 'a': 1, 'b': 2 }
Idea: Use Array.reduce to traverse the array. For each element in the array, deconstruct it into key and val, and add it to a new object. Finally, you will get an object containing all key-value pairs.
indexOf
Returns the subscript of an element in the array, looking from front to back
const indexOf = (arr, val, fromIndex = 0) =>
arr.findIndex((item, index) => index >= fromIndex && item === val);
indexOf([1, 2, 1, 2], 2);
// => 1
// Search from the `fromIndex`.
indexOf([1, 2, 1, 2], 2, 2);
// => 3
Idea: Use the findIndex method of the array to search, and support searching starting from the specified index position.
initial
Returns a new array with the last element removed from the original array
const initial = arr => arr.slice(0, -1);
initial([1, 2, 3]);
// => [1, 2]
Idea: Use the slice method of the array to intercept the part except the last element and get a new array.
intersection
Returns an array containing elements present in all arrays
const intersection = (...arr) => [
...new Set(arr.reduce((a, b) => a.filter(v => b.includes(v)))),
];
intersection([2, 1], [4, 2], [1, 2]);
// => [2]
Idea: Use the reduce method of the array to traverse all arrays, use the filter method to filter out elements that also exist in the current array, and finally use Set to remove duplicates and convert them into an array.
join
Convert an array to a string, separated by the specified delimiter
const join = (arr, separator = ',') =>
arr.reduce((res, val, i) => `${res}${i ? separator : ''}${val}`, '');
join(['a', 'b', 'c'], '~');
// => 'a~b~c'
Idea: Use the reduce method to traverse the array, concatenate each element and delimiter, and finally get a concatenated string.
last
Returns the last element in the array
const last = arr => arr[arr.length - 1];
last([1, 2, 3]);
// => 3
Idea: Return the last element in the array.
lastIndexOf
Returns the subscript of an element in the array, looking from back to front
const lastIndexOf = (arr, val) => arr.lastIndexOf(val);
lastIndexOf([1, 2, 1, 2], 2);
// => 3
// Search from the `fromIndex`.
lastIndexOf([1, 2, 1, 2], 2, 2);
// => 1
Idea: Use the lastIndexOf method of the array to find the subscript of the element in the array.
pull
Remove the specified element from the array
const pull = (arr, ...args) => arr.filter(item => !args.includes(item));
const array = [1, 2, 3, 1, 2, 3];
pull(array, 2, 3);
console.log(array);
// => [1, 1]
Idea: Use the filter method to filter out unnecessary elements.
pullAt
Removes the element at the specified subscript from the array and returns a new array
const pullAt = (arr, ...args) => args.map(index => arr.splice(index, 1)[0]);
const array = [5, 10, 15, 20];
const evens = pullAt(array, 1, 3);
console.log(array);
// => [5, 15]
console.log(evens);
// => [10, 20]
Idea: The map() method traverses the incoming subscript array, deletes the corresponding elements from the original array through the splice() method and returns it.
reverse
Reverse an array
const reverse = arr => [...arr].reverse();
const array = [1, 2, 3];
reverse(array);
// => [3, 2, 1]
console.log(array);
// => [3, 2, 1]
Idea: Just use destructuring assignment and reverse() method.
slice
Returns a new array, intercepting the specified range of elements from the original array
const slice = (arr, start, end) => arr.slice(start, end);
Idea: Call the native slice() method directly.
sortedIndex
Returns the index into which an element should be inserted into the array
const sortedIndex = (arr, value) => {
let left = 0;
let right = arr.length;
while (left < right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] < value) {
left = mid + 1;
} else {
right = mid;
}
}
return right;
};
sortedIndex([30, 50], 40);
// => 1
Idea: Implement binary search algorithm to find the position where the element should be inserted.
tail
Returns a new array, removing the first element from the original array
const tail = arr => arr.slice(1);
tail([1, 2, 3]);
// => [2, 3]
Idea: Use the slice() method to remove the first element.
take
Returns a new array containing the first n elements of the original array
const take = (arr, n = 1) => arr.slice(0, n);
take([1, 2, 3]);
// => [1]
take([1, 2, 3], 2);
// => [1, 2]
take([1, 2, 3], 5);
// => [1, 2, 3]
take([1, 2, 3], 0);
// => []
Idea: Call the native slice() method directly, paying attention to the default parameters.
takeRight
Returns a new array containing the last n elements of the original array
const takeRight = (arr, n = 1) => arr.slice(-n);
takeRight([1, 2, 3]);
// => [3]
takeRight([1, 2, 3], 2);
// => [2, 3]
takeRight([1, 2, 3], 5);
// => [1, 2, 3]
takeRight([1, 2, 3], 0);
// => []
Idea: Also call the native slice() method directly, paying attention to the use of negative subscripts.
union
Returns a new array containing all unique elements in the array
const union = (...args) => [...new Set(args.flat())];
union([2], [1, 2]);
// => [2, 1]
Idea: The flat() method converts a multi-dimensional array into one dimension, the Set data structure removes duplication, and then converts it back to an array.
uniq
Returns a new array containing all unique elements in the array
const uniq = arr => [...new Set(arr)];
uniq([2, 1, 2]);
// => [2, 1]
Idea: Also use the Set() data structure to remove duplicates.
without
Returns a new array, removing the specified elements from the original array
const without = (arr, ...args) => arr.filter(item => !args.includes(item));
without([2, 1, 2, 3], 1, 2);
// => [3]
Idea: Same as pull method.
xor
Returns a new array containing elements that appear in only one of the arrays
const xor = (...args) =>
args
.flat()
.filter(
item => args.flat().indexOf(item) === args.flat().lastIndexOf(item)
);
xor([2, 1], [2, 3]);
// => [1, 3]
Idea: The flat() method converts it into a one-dimensional array, and then uses the filter() method and the indexOf() and lastIndexOf() methods to determine the elements that only appear in one array.
zip
Combine elements at the same position in multiple arrays into one array
const zip = (...arrays) =>
arrays[0].map((_, i) => arrays.map(array => array[i]));
zip(['fred', 'barney'], [30, 40], [true, false]);
// => [['fred', 30, true], ['barney', 40, false]]
Idea: Using the Rest parameter, first take out the first array, then use map to traverse the length of the first array, traverse the index elements of all arrays by index, combine them into a new array and return it.
unzip
Restore the array generated by the zip function to the original array
const unzip = array =>
array.reduce(
(acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc),
Array.from({ length: Math.max(...array.map(a => a.length)) }).map(() => [])
);
const zipped = zip(['fred', 'barney'], [30, 40], [true, false]);
// => [['fred', 30, true], ['barney', 40, false]]
unzip(zipped);
// => [['fred', 'barney'], [30, 40], [true, false]]
Idea: Use reduce to traverse the array generated by the zip function, take out each value of each element, and form a new array based on the index to return. In the initial value of the reduce function, Math.max is used to obtain the maximum length of all elements, and Array.from is used to create a two-dimensional array of the corresponding length.
dropWhile
Returns a new array, removing the elements from the beginning to the first element that meets the criteria in the original array
const dropWhile = (array, predicate) =>
array.slice(array.findIndex(val => !predicate(val)));
const users = [
{ user: 'barney', active: false },
{ user: 'fred', active: false },
{ user: 'pebbles', active: true },
];
dropWhile(users, o => !o.active);
// => objects for ['pebbles']
// The `matches` iteratee shorthand.
dropWhile(users, { user: 'barney', active: false });
// => objects for ['fred', 'pebbles']
// The `matchesProperty` iteratee shorthand.
dropWhile(users, ['active', false]);
// => objects for ['pebbles']
// The `property` iteratee shorthand.
dropWhile(users, 'active');
// => objects for ['barney', 'fred', 'pebbles']
Idea: Use the findIndex function to find the first element that does not meet the conditions, and then use the slice function to intercept the array after the element and return it.
intersectionBy
Similar to intersection, but you can specify a function to compare elements in the array
const intersectionBy = (arr1, arr2, compareFn) =>
arr1.filter(item =>
arr2.some(compareItem => compareFn(item) === compareFn(compareItem))
);
intersectionBy([2.1, 1.2], [4.3, 2.4], Math.floor);
// => [2.1]
// The `property` iteratee shorthand.
intersectionBy([{ x: 1 }], [{ x: 2 }, { x: 1 }], 'x');
// => [{ 'x': 1 }]
Idea: Use the filter method of the array to traverse the first array, and use the function specified by the parameter to compare each element of the second array, and finally return the intersection of the two arrays.
pullAll
Similar to pull, but takes an array as argument
const pullAll = (arr, values) => arr.filter(item => !values.includes(item));
const array = [1, 2, 3, 1, 2, 3];
pullAll(array, [2, 3]);
console.log(array);
// => [1, 1]
Idea: Use the filter method of the array to traverse the original array and exclude elements that exist in the incoming array.
pullAllBy
Similar to pullBy, but takes an array as parameter
const pullAllBy = (arr, values, compareFn) =>
arr.filter(
item =>
!values.some(compareItem => compareFn(item) === compareFn(compareItem))
);
const array = [{ x: 1 }, { x: 2 }, { x: 3 }, { x: 1 }];
pullAllBy(array, [{ x: 1 }, { x: 3 }], 'x');
console.log(array);
// => [{ 'x': 2 }]
Idea: Use the filter method of the array to traverse the original array, exclude elements that exist in the passed array, and use the parameters
The function specified by the number compares the elements in two arrays.
pullAllWith
Similar to pullWith, but takes an array as argument
const pullAllWith = (arr, values, compareFn) =>
arr.filter(item => !values.some(compareItem => compareFn(item, compareItem)));
const array = [
{ x: 1, y: 2 },
{ x: 3, y: 4 },
{ x: 5, y: 6 },
];
pullAllWith(array, [{ x: 3, y: 4 }], isEqual);
console.log(array);
// => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
Idea: Use the filter method of the array to traverse the original array, exclude elements that exist in the incoming array, and use the function specified by the parameter to compare the elements in the two arrays.
sortedIndexOf
Like indexOf, but can be used on sorted arrays
const sortedIndexOf = (arr, value) => {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === value) {
return mid;
}
if (arr[mid] < value) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
};
sortedIndexOf([4, 5, 5, 5, 6], 5);
// => 1
Idea: Use the binary search algorithm to find the position of the specified element in the sorted array.
sortedLastIndexOf
Like lastIndexOf, but can be used in sorted arrays
const sortedLastIndexOf = (arr, value) => {
let left = 0;
let right = arr.length - 1;
let lastIndex = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === value) {
lastIndex = mid;
left = mid + 1;
} else if (arr[mid] < value) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return lastIndex;
};
sortedLastIndex([4, 5, 5, 5, 6], 5);
// => 4
Idea: Use the binary search algorithm to find the last position of the specified element in the sorted array.
sortedUniq
Like uniq, but can be used with sorted arrays
const sortedUniq = arr =>
arr.reduce((result, item) => {
if (result.length === 0 || result[result.length - 1] !== item) {
result.push(item);
}
return result;
}, []);
sortedUniq([1, 1, 2]);
// => [1, 2]
Idea: Use the reduce method and indexOf method of the array to filter out duplicate elements and return a new array.
sortedUniqBy
Like uniqBy, but can be used with sorted arrays
const sortedUniqBy = (array, iteratee) =>
array.reduce(
(result, value) =>
result.length && iteratee(value) === iteratee(result[result.length - 1])
?result
: [...result, value],
[]
);
sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
// => [1.1, 2.3]
Idea: Use the reduce method to traverse the original array, convert each element into a value through the specified function, and use the Set object to remove duplication, and finally return the deduplicated array.
takeWhile
Returns a new array containing the elements from the beginning to the first element that does not meet the criteria in the original array
const takeWhile = (array, predicate) =>
array.slice(
0,
array.findIndex(element => !predicate(element))
);
Idea: Use the findIndex method to find the index of the first element that does not meet the conditions, and then use the slice method to intercept the part of the original array that meets the conditions and return a new array.
takeRightWhile
Returns a new array containing the elements from the last unqualified element to the end of the original array
const takeRightWhile = (array, predicate) =>
array
.reverse()
.slice(
0,
array.findIndex(element => !predicate(element))
)
.reverse();
const users = [
{ user: 'barney', active: true },
{ user: 'fred', active: false },
{ user: 'pebbles', active: false },
];
takeRightWhile(users, o => !o.active);
// => objects for ['fred', 'pebbles']
// The `matches` iteratee shorthand.
takeRightWhile(users, { user: 'pebbles', active: false });
// => objects for ['pebbles']
// The `matchesProperty` iteratee shorthand.
takeRightWhile(users, ['active', false]);
// => objects for ['fred', 'pebbles']
// The `property` iteratee shorthand.
takeRightWhile(users, 'active');
// => []
Idea: Use the reverse method to reverse the original array, then use the findIndex method to find the index of the first element in the reversed array that does not meet the conditions, and finally use the slice method to intercept the part from the index to the end of the original array and return the new array .
unionBy
Similar to union, but you can specify a function to compare the elements in the array
const unionBy = (...arrays) => {
const iteratee = arrays.pop();
const unionSet = new Set(
arrays.reduce((result, array) => [...result, ...array], []).map(iteratee)
);
return Array.from(unionSet);
};
unionBy([2.1], [1.2, 2.3], Math.floor);
// => [2.1, 1.2]
// The `property` iteratee shorthand.
unionBy([{ x: 1 }], [{ x: 2 }, { x: 1 }], 'x');
// => [{ 'x': 1 }, { 'x': 2 }]
Idea: Use the Set object to deduplicate arrays, convert elements in multiple arrays into the same value by specifying functions, and finally convert the deduplicated values into arrays and return them.
uniqBy
Similar to uniq, but you can specify a function to compare the elements in the array
const uniqBy = (array, iteratee) => {
const uniqSet = new Set(array.map(iteratee));
return Array.from(uniqSet).map(value =>
array.find(element => iteratee(element) === value)
);
};
uniqBy([2.1, 1.2, 2.3], Math.floor);
// => [2.1, 1.2]
// The `property` iteratee shorthand.
uniqBy([{ x: 1 }, { x: 2 }, { x: 1 }], 'x');
// => [{ 'x': 1 }, { 'x': 2 }]
Idea: Similar to the implementation idea of unionBy method.
unzipWith
Similar to unzip, but you can specify a function to process the array generated by the zip function
const unzipWith = (array, iteratee) =>
array.reduce(
(result, value) => (
value.forEach((innerValue, index) =>
result[index].push(iteratee ? iteratee(innerValue) : innerValue)
),
result
),
Array.from({ length: Math.max(...array.map(value => value.length)) }).map(
() => []
)
);
const zipped = zip([1, 2], [10, 20], [100, 200]);
// => [[1, 10, 100], [2, 20, 200]]
unzipWith(zipped, add);
// => [3, 30, 300]
Idea: Use the reduce method to traverse the incoming array, process each sub-array with the incoming function, store the processed value in the corresponding index position, and finally return the processed array.
xorBy
Similar to xor, but you can specify a function to compare elements in the array
const xorBy = (...arrays) => {
const iteratee = arrays.pop();
const valueCount = new Map();
arrays.reduce((result, array) => [...result, ...array], [])
.forEach((value) => {
const newValue = iteratee(value);
xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);
// => [1.2, 3.4]
// The `property` iteratee shorthand.
xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
// => [{ 'x': 2 }]
Idea: Use the reduce method to traverse the multiple incoming arrays, convert the elements in each array into specified values, use the Map object to count the number of occurrences of each value, and finally return the elements consisting of elements with the number of occurrences 1 array.
zipObject
Convert two arrays into one object
const zipObject = (keys, values) =>
keys.reduce((obj, key, i) => ({ ...obj, [key]: values[i] }), {});
zipObject(['a', 'b'], [1, 2]);
// => { 'a': 1, 'b': 2 }
Idea: Use the reduce function to traverse the keys array, each time using an element in the keys array as the attribute name, and the elements at the same position in the values array as the attribute values, then store them in a new object, and finally return the object.
zipObjectDeep
Convert two arrays into a nested object
const zipObjectDeep = (keys, values) =>
keys.reduce((obj, key, i) => {
const path = key.split('.');
const lastKey = path.pop();
const nestedObj = path.reduceRight(
(nested, prop) => ({ [prop]: nested }),
values[i]
);
return mergeWith(obj, { [lastKey]: nestedObj }, customizer);
}, {});
zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);
// => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
Idea: Use the reduce function to traverse the keys array, split an element in the keys array into a path array each time, use the reduceRight function to start traversing from the last element of the path array, and use this element as the attribute name each time, and the previous element The corresponding objects are used as property values, then they are stored in a new object, and finally the object is returned.
gather
countBy
Count the number of occurrences of each element in an array
const countBy = (arr, fn) =>
arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => {
acc[val] = (acc[val] || 0) + 1;
return acc;
}, {});
countBy([6.1, 4.2, 6.3], Math.floor);
// => { '4': 1, '6': 2 }
// The `property` iteratee shorthand.
countBy(['one', 'two', 'three'], 'length');
// => { '3': 2, '5': 1 }
Idea: Use map to map each element of the array. When fn is a function, execute it directly. Otherwise, take the corresponding attribute, and then use reduce to accumulate the number of occurrences of the element.
each
Iterates over an array or object and performs the specified function on each element
// each -> forEach
const each = (collection, fn) => {
if (Array.isArray(collection)) {
for (let i = 0; i < collection.length; i++) {
fn(collection[i], i, collection);
}
} else {
for (const key in collection) {
if (collection.hasOwnProperty(key)) {
fn(collection[key], key, collection);
}
}
}
};
forEach([1, 2], value => {
console.log(value);
});
// => Logs `1` then `2`.
forEach({ a: 1, b: 2 }, (value, key) => {
console.log(key);
});
// => Logs 'a' then 'b' (iteration order is not guaranteed).
Idea: If it is an array, use a for loop to traverse and execute the fn function on each element; if it is an object, use a for-in loop to traverse and execute the fn function on each attribute.
filter
Traverse an array or object and return elements that meet the criteria
const filter = (collection, predicate) =>
collection.filter(
typeof predicate === 'function' ? predicate : val => val[predicate]
);
const users = [
{ user: 'barney', age: 36, active: true },
{ user: 'fred', age: 40, active: false },
];
filter(users, o => !o.active);
// => objects for ['fred']
// The `matches` iteratee shorthand.
filter(users, { age: 36, active: true });
// => objects for ['barney']
// The `matchesProperty` iteratee shorthand.
filter(users, ['active', false]);
// => objects for ['fred']
// The `property` iteratee shorthand.
filter(users, 'active');
// => objects for ['barney']
Idea: Use the filter method to filter elements that meet the conditions. When predicate is a function, execute it directly. Otherwise, determine whether the element has the corresponding attribute value.
find
Traverse an array or object and return the first element that meets the criteria
const find = (collection, predicate) =>
collection.filter(
typeof predicate === 'function' ? predicate : val => val[predicate]
)[0];
const users = [
{ user: 'barney', age: 36, active: true },
{ user: 'fred', age: 40, active: false },
{ user: 'pebbles', age: 1, active: true },
];
find(users, o => o.age < 40);
// => object for 'barney'
// The `matches` iteratee shorthand.
find(users, { age: 1, active: true });
// => object for 'pebbles'
// The `matchesProperty` iteratee shorthand.
find(users, ['active', false]);
// => object for 'fred'
// The `property` iteratee shorthand.
find(users, 'active');
// => object for 'barney'
Idea: Use the filter method to filter elements that meet the conditions and return the first element that meets the conditions. When predicate is a function, it is executed directly. Otherwise, it is judged whether the element has the corresponding attribute value.
flatMap
Traverse the array, map each element into a new array, and then merge multiple arrays into a new array
const flatMap = (arr, fn) => arr.reduce((acc, val) => acc.concat(fn(val)), []);
function duplicate(n) {
return [n, n];
}
flatMap([1, 2], duplicate);
// => [1, 1, 2, 2]
Idea:
flatMapDeep
Traverse the array, map each element into a new array, proceed recursively, and then merge multiple arrays into a new array
const flatMapDeep = (arr, fn) =>
arr.reduce(
(acc, val) =>
acc.concat(Array.isArray(val) ? flatMapDeep(val, fn) : fn(val)),
[]
);
function duplicate(n) {
return [[[n, n]]];
}
flatMapDeep([1, 2], duplicate);
// => [1, 1, 2, 2]
Idea: Use reduce to traverse the array, map each element into a new array, recursively if it is an array, and finally use concat to merge multiple arrays into a new array.
flatMapDepth
Traverse the array, map each element into a new array, specify the recursion depth, and then merge multiple arrays into a new array
const flatMapDepth = (array, iteratee, depth = 1) =>
depth > 0
? array.reduce((acc, cur) => {
const mapped = iteratee(cur);
return acc.concat(
Array.isArray(mapped)
? flatMapDepth(mapped, iteratee, depth - 1)
: mapped
);
}, [])
: array.slice();
function duplicate(n) {
return [[[n, n]]];
}
flatMapDepth([1, 2], duplicate, 2);
// => [[1, 1], [2, 2]]
Idea: Use recursion to traverse the array depth-first, then map each element into a new array, and finally merge multiple arrays into a new array.
forEach
Iterates over an array or object and performs the specified function on each element, similar to the each function
const forEach = (collection, iteratee) => {
for (const value of collection) {
iteratee(value);
}
};
_([1, 2]).forEach(value => {
console.log(value);
});
// => Logs `1` then `2`.
forEach({ a: 1, b: 2 }, (value, key) => {
console.log(key);
});
// => Logs 'a' then 'b' (iteration order is not guaranteed).
Idea: Use for...of to loop through the array or object and execute the specified function on each element.
groupBy: Group the array in the specified way
const groupBy = (array, iteratee) =>
array.reduce((acc, cur) => {
const key = typeof iteratee === 'function' ? iteratee(cur) : cur[iteratee];
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(cur);
return acc;
}, {});
groupBy([6.1, 4.2, 6.3], Math.floor);
// => { '4': [4.2], '6': [6.1, 6.3] }
// The `property` iteratee shorthand.
groupBy(['one', 'two', 'three'], 'length');
// => { '3': ['one', 'two'], '5': ['three'] }
Idea: Use the reduce method to traverse the array and group the array in the specified way.
includes
Determine whether an element is in an array or object
const includes = (collection, value) => collection.includes(value);
includes([1, 2, 3], 1);
// => true
includes([1, 2, 3], 1, 2);
// => false
includes({ user: 'fred', age: 40 }, 'fred');
// => true
includes('pebbles', 'eb');
// => true
Idea: Use the Array.includes method to determine whether an element is in an array or object.
invokeMap
Call the specified method on each element in the array and return the result
const invokeMap = (array, path, ...args) =>
array.map(value => {
const method = typeof path === 'function' ? path : value[path];
return method.apply(value, args);
});
invokeMap(
[
[5, 1, 7],
[3, 2, 1],
],
'sort'
);
// => [[1, 5, 7], [1, 2, 3]]
invokeMap([123, 456], String.prototype.split, '');
// => [['1', '2', '3'], ['4', '5', '6']]
Idea: Use the Array.map method to call the specified method on each element in the array and return the result.
keyBy
Convert the array into an object. The key value of the object is the value of the specified attribute, and the value is the element.
const keyBy = (arr, key) =>
arr.reduce((acc, val) => ((acc[val[key]] = val), acc), {});
const array = [
{ dir: 'left', code: 97 },
{ dir: 'right', code: 100 },
];
keyBy(array, o => String.fromCharCode(o.code));
// => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
keyBy(array, 'dir');
// => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
Idea: Use the reduce method to traverse the array and convert the array into an object. The key value of the object is the value of the specified attribute, and the value is the element.
map: Traverse the array or object and map each element into a new element
const map = (arr, func) => arr.reduce((acc, val) => [...acc, func(val)], []);
function square(n) {
return n * n;
}
map([4, 8], square);
// => [16, 64]
map({ a: 4, b: 8 }, square);
// => [16, 64] (iteration order is not guaranteed)
const users = [{ user: 'barney' }, { user: 'fred' }];
// The `property` iteratee shorthand.
map(users, 'user');
// => ['barney', 'fred']
Idea: Use the reduce method to traverse the array and map each element into a new element.
orderBy
Sort an array in a specified way
const orderBy = (arr, props, orders) =>
[...arr].sort((a, b) =>
props.reduce((acc, prop, i) => {
if (acc === 0) {
const [p1, p2] =
orders && orders[i] === 'desc'
? [b[prop], a[prop]]
: [a[prop], b[prop]];
acc = p1 > p2 ? 1 : p1 < p2 ? -1 : 0;
}
return acc;
}, 0)
);
const users = [
{ user: 'fred', age: 48 },
{ user: 'barney', age: 34 },
{ user: 'fred', age: 40 },
{ user: 'barney', age: 36 },
];
// Sort by `user` in ascending order and `age` in descending order.
orderBy(users, ['user', 'age'], ['asc', 'desc']);
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
Idea: Use the sort method to sort the array. The props parameter represents the sorting properties, and the orders parameter represents the sorting order.
partition
Split the array according to specified conditions
const partition = (arr, func) =>
arr.reduce((acc, val) => (acc[func(val) ? 0 : 1].push(val), acc), [[], []]);
const users = [
{ user: 'barney', age: 36, active: false },
{ user: 'fred', age: 40, active: true },
{ user: 'pebbles', age: 1, active: false },
];
partition(users, o => o.active);
// => objects for [['fred'], ['barney', 'pebbles']]
// The `matches` iteratee shorthand.
partition(users, { age: 1, active: false });
// => objects for [['pebbles'], ['barney', 'fred']]
// The `matchesProperty` iteratee shorthand.
partition(users, ['active', false]);
// => objects for [['barney', 'pebbles'], ['fred']]
// The `property` iteratee shorthand.
partition(users, 'active');
// => objects for [['fred'], ['barney', 'pebbles']]
Idea: Use the reduce method to traverse the array and split the array according to the specified conditions.
reduce
Iterate over an array or object and add each element to the accumulator
const reduce = (arr, func, initVal) => {
let acc = initVal;
for (let i = 0; i < arr.length; i++) {
acc = func(acc, arr[i], i, arr);
}
return acc;
};
reduce([1, 2], (sum, n) => sum + n, 0);
// => 3
reduce(
{ a: 1, b: 2, c: 1 },
(result, value, key) => {
(result[value] || (result[value] = [])).push(key);
return result;
},
{}
);
// => { '1': ['a', 'c'], '2': ['b'] } (The order of traversal cannot be guaranteed)
Idea: Use a for loop to traverse the array and add each element to the accumulator.
reduceRight
Similar to reduce, but starts from the end of the array
const reduceRight = (arr, func, initVal) => {
let acc = initVal;
for (let i = arr.length - 1; i >= 0; i--) {
acc=func(acc, arr[i], i, arr);
}
return acc;
};
const array = [
[0, 1],
[twenty three],
[4, 5],
];
reduceRight(array, (flattened, other) => flattened.concat(other), []);
// => [4, 5, 2, 3, 0, 1]
Idea: Similar to reduce, but starts traversing from the end of the array.
reject
Traverse an array or object and return elements that do not meet the criteria
const reject = (arr, fn) => arr.filter(x => !fn(x));
const users = [
{ user: 'barney', age: 36, active: false },
{ user: 'fred', age: 40, active: true },
];
reject(users, o => !o.active);
// => objects for ['fred']
// `matches` iteration abbreviation
reject(users, { age: 40, active: true });
// => objects for ['barney']
// `matchesProperty` shorthand for iteration
reject(users, ['active', false]);
// => objects for ['fred']
// `property` shorthand for iteration
reject(users, 'active');
// => objects for ['barney']
Idea: Use the filter method to traverse the array and return elements that do not meet the conditions.
sample
Returns a random element from an array or object
const sample = arr => arr[Math.floor(Math.random() * arr.length)];
sample([1, 2, 3, 4]);
// => 2
Idea: Use the Math.random method to generate a random number, and then randomly obtain an element based on the length of the array.
sampleSize
Returns multiple elements in an array or object at random
const sampleSize = (arr, n = 1) =>
arr.sort(() => Math.random() - 0.5).slice(0, n);
sampleSize([1, 2, 3], 2);
// => [3, 1]
sampleSize([1, 2, 3], 4);
// => [2, 3, 1]
Idea: Use the sort method and Math.random method to disrupt the order of the array, and then use the slice method to intercept a specified number of elements.
shuffle
Randomly shuffle elements in an array or object
const shuffle = arr => arr.sort(() => Math.random() - 0.5);
shuffle([1, 2, 3, 4]);
// => [4, 1, 3, 2]
Idea: Use the sort method and Math.random method to disrupt the order of the array.
size
Returns the length or number of elements of an array or object
const size = obj => Object.keys(obj).length;
size([1, 2, 3]);
// => 3
size({ a: 1, b: 2 });
// => 2
size('pebbles');
// => 7
Idea: Use the Object.keys method to get the array of object properties, and then use the length property to get the length.
some
Traverse the array or object to determine whether at least one element meets the conditions
const some = (arr, fn) => arr.some(fn);
some([null, 0, 'yes', false], Boolean);
// => true
const users = [
{ user: 'barney', active: true },
{ user: 'fred', active: false },
];
// The `matches` iteratee shorthand.
some(users, { user: 'barney', active: false });
// => false
// The `matchesProperty` iteratee shorthand.
some(users, ['active', false]);
// => true
// The `property` iteratee shorthand.
some(users, 'active');
// => true
Idea: Use some method to determine whether at least one element meets the conditions.
sortBy
Sort an array in a specified way
const sortBy = (arr, fn) => arr.sort((a, b) => fn(a) - fn(b));
const users = [
{ user: 'fred', age: 48 },
{ user: 'barney', age: 36 },
{ user: 'fred', age: 40 },
{ user: 'barney', age: 34 },
];
sortBy(users, o => o.user);
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
sortBy(users, ['user', 'age']);
// => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]
sortBy(users, 'user', o => Math.floor(o.age / 10));
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
Idea: Use the sort method and the passed-in function to sort.
Function
after
Specifies how many times a function will be called before it is executed
const after = (n, func) => {
let count = 0;
return (...args) => {
count++;
if (count >= n) {
return func(...args);
}
};
};
const saves = ['profile', 'settings'];
const done = after(saves.length, () => {
console.log('done saving!');
});
forEach(saves, type => {
asyncSave({ type, complete: done });
});
// => Logs 'done saving!' after the two async saves have completed.
Idea: Return a function that executes the specified function after being called n times. Use closures to record the current number of calls and determine whether execution conditions are met.
ary: Encapsulate the specified function and specify the maximum number of parameters it can receive.
const ary =
(func, n = func.length) =>
(...args) =>
func(...args.slice(0, n));
map(['6', '8', '10'], ary(parseInt, 1));
// => [6, 8, 10]
Idea: Encapsulate the specified function and specify it to receive up to n parameters. Returns a new function, limiting the number of function parameters.
before: Specify how many times a function will be executed before being called.
const before = (n, func) => {
let count = 0;
return (...args) => {
count++;
if (count < n) {
return func(...args);
}
};
};
jQuery(element).on('click', before(5, addContactToList));
// => Allows adding up to 4 contacts to the list.
Idea: Return a function that executes the specified function before being called n times. Use closures to record the current number of calls and determine whether execution conditions are met.
bind
Binds the function's this value to the specified parameters and returns a new function
const bind =
(func, thisArg, ...boundArgs) =>
(...args) =>
func.apply(thisArg, [...boundArgs, ...args]);
const greet = function (greeting, punctuation) {
return `${greeting} ${this.user}${punctuation}`;
};
const object = { user: 'fred' };
var bound = bind(greet, object, 'hi');
bound('!');
// => 'hi fred!'
// Bound with placeholders.
var bound = bind(greet, object, _, '!');
bound('hi');
// => 'hi fred!'
Idea: Bind the this value of the function and the specified parameters, and return a new function. Implemented using apply and bind methods.
bindKey
Similar to bind, but binds the specified method on the object
const bindKey =
(object, key, ...args) =>
(...args2) =>
object[key].apply(object, [...args, ...args2]);
const object = {
user: 'fred',
greet(greeting, punctuation) {
return `${greeting} ${this.user}${punctuation}`;
},
};
var bound = bindKey(object, 'greet', 'hi');
bound('!');
// => 'hi fred!'
object.greet = function (greeting, punctuation) {
return `${greeting}ya ${this.user}${punctuation}`;
};
bound('!');
// => 'hiya fred!'
// Bound with placeholders.
var bound = bindKey(object, 'greet', _, '!');
bound('hi');
// => 'hiya fred!'
Idea: Similar to bind, but it binds the specified method on the object. Implemented using apply and bind methods.
curry
Curry the specified function
const curry = (func, arity = func.length, ...args) =>
arity <= args.length ? func(...args) : curry.bind(null, func, arity, ...args);
const abc = function (a, b, c) {
return [a, b, c];
};
const curried = curry(abc);
curried(1)(2)(3);
// => [1, 2, 3]
curried(1, 2)(3);
// => [1, 2, 3]
curried(1, 2, 3);
// => [1, 2, 3]
// Curried with placeholders.
curried(1)(_, 3)(2);
// => [1, 2, 3]
Idea: Curry the specified function. Return a new function. When the number of parameters is insufficient, continue to return a new function until the number of parameters is sufficient to execute the original function.
curryRight
Like curry, but handles arguments from right to left
const curryRight = (func, arity = func.length, ...args) =>
arity <= args.length
? func(...args.reverse())
: curryRight.bind(null, func, arity, ...args);
const abc = function (a, b, c) {
return [a, b, c];
};
const curried = curryRight(abc);
curried(3)(2)(1);
// => [1, 2, 3]
curried(2, 3)(1);
// => [1, 2, 3]
curried(1, 2, 3);
// => [1, 2, 3]
// Curried with placeholders.
curried(3)(1, _)(2);
// => [1, 2, 3]
Idea: Similar to curry, but parameters are processed from right to left.
debounce
Perform anti-shake processing on the specified function
const debounce = (func, wait, immediate = false) => {
let timeoutId;
return (...args) => {
if (immediate && !timeoutId) {
func(...args);
}
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
if (!immediate) {
func(...args);
}
timeoutId = null;
}, wait);
};
};
// Avoid expensive calculation overhead when the window changes.
jQuery(window).on('resize', debounce(calculateLayout, 150));
// `sendMail` is then called when clicked.
jQuery(element).on(
'click',
debounce(sendMail, 300, {
leading: true,
trailing: false,
})
);
// Ensure that `batchLog` will be triggered within 1 second after calling it once.
const debounced = debounce(batchLog, 250, { maxWait: 1000 });
const source = new EventSource('/stream');
jQuery(source).on('message', debounced);
// Cancel a trailing anti-shake call
jQuery(window).on('popstate', debounced.cancel);
Idea: Perform anti-shake processing on the specified function. Returns a new function that is executed only once for a period of time.
defer
Delay the execution of the specified function
const defer = (func, ...args) => setTimeout(func, 1, ...args);
defer(text => {
console.log(text);
}, 'deferred');
// => Output 'deferred' for one millisecond or more.
Idea: Delay the execution of the specified function. Implemented using setTimeout.
delay
Delay the execution of the specified function for a period of time
const delay = (func, wait, ...args) => setTimeout(func, wait, ...args);
delay(
text => {
console.log(text);
},
1000,
'later'
);
// => Output 'later' after one second.
Idea: Delay the execution of the specified function for a period of time. Implemented using setTimeout.
flip
Reverse the parameters of the specified function
const flip =
fn =>
(...args) =>
fn(...args.reverse());
const flipped = flip(function () {
return toArray(arguments);
});
flipped('a', 'b', 'c', 'd');
// => ['d', 'c', 'b', 'a']
Idea: Reverse the order of parameters of the function and return a new function
memoize
Memorize the specified function and cache the calculation results of the function
const memoize = fn => {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
return cache.has(key)
? cache.get(key)
: cache.set(key, fn(...args)).get(key);
};
};
const object = { a: 1, b: 2 };
const other = { c: 3, d: 4 };
var values = memoize(values);
values(object);
// => [1, 2]
values(other);
// => [3, 4]
object.a = 2;
values(object);
// => [1, 2]
// Modify the result cache.
values.cache.set(object, ['a', 'b']);
values(object);
// => ['a', 'b']
// Replace `memoize.Cache`.
memoize.Cache = WeakMap;
Idea: cache the calculation results of the function and return a new function
negate
Encapsulate the specified function and return the negative value of the original function
const negate =
fn =>
(...args) =>
!fn(...args);
function isEven(n) {
return n % 2 == 0;
}
filter([1, 2, 3, 4, 5, 6], negate(isEven));
// => [1, 3, 5]
Idea: Return a new function that inverts the result of executing the original function
once
Specifies that a function can only be called once
const once = fn => {
let called = false;
return (...args) => {
if (!called) {
called = true;
return fn(...args);
}
};
};
const initialize = once(createApplication);
initialize();
initialize();
// `initialize` can only call `createApplication` once.
Idea: Return a new function, which can only be called once
overArgs
Encapsulate the specified function and convert the form of the parameters
const overArgs =
(fn, transforms) =>
(...args) =>
fn(...args.map((arg, index) => transforms[index](arg)));
function doubled(n) {
return n * 2;
}
function square(n) {
return n * n;
}
const func = overArgs((x, y) => [x, y], [square, doubled]);
func(9, 3);
// => [81, 6]
func(10, 5);
// => [100, 10]
Idea: Return a new function that converts the specified parameters of the original function
partial
Partially apply the specified function and specify some parameters
const partial =
(fn, ...args) =>
(...newArgs) =>
fn(...args, ...newArgs);
const greet = function (greeting, name) {
return `${greeting} ${name}`;
};
const sayHelloTo = partial(greet, 'hello');
sayHelloTo('fred');
// => 'hello fred'
// Placeholders are used.
const greetFred = partial(greet, _, 'fred');
greetFred('hi');
// => 'hi fred'
Idea: Return a new function that partially applies the specified parameters of the original function
partialRight
Similar to partial, but partial arguments are specified from right to left
const partialRight =
(fn, ...args) =>
(...newArgs) =>
fn(...newArgs, ...args);
const greet = function (greeting, name) {
return `${greeting} ${name}`;
};
const greetFred = partialRight(greet, 'fred');
greetFred('hi');
// => 'hi fred'
// Placeholders are used.
const sayHelloTo = partialRight(greet, 'hello', _);
sayHelloTo('fred');
// => 'hello fred'
Idea: Return a new function that starts from Apply the specified parameters of the original function from right to left
rearg
Encapsulate the specified function and adjust the position of the parameters
const rearg =
(fn, indexes) =>
(...args) =>
fn(...indexes.map(index => args[index]));
const rearged = rearg((a, b, c) => [a, b, c], [2, 0, 1]);
rearged('b', 'c', 'a');
// => ['a', 'b', 'c']
Idea: Return a new function that adjusts the parameter order of the original function
rest
Encapsulate the specified function, collect the parameters into an array and pass it into the original function
const rest =
fn =>
(...args) =>
fn(args);
const say = rest(
(what, names) =>
`${what} ${initial(names).join(', ')}${size(names) > 1 ? ', & ' : ''}${last(
names
)}`
);
say('hello', 'fred', 'barney', 'pebbles');
// => 'hello fred, barney, & pebbles'
Idea: Return a new function that collects the parameters of the original function into an array and passes it in
spread
Encapsulate the specified function, expand the parameter array and pass it into the original function as multiple parameters.
const spread = fn => args => fn(...args);
const say = spread((who, what) => `${who} says ${what}`);
say(['fred', 'hello']);
// => 'fred says hello'
const numbers = Promise.all([Promise.resolve(40), Promise.resolve(36)]);
numbers.then(spread((x, y) => x + y));
// => a Promise of 76
Idea: Return a new function that expands the parameter array of the original function and passes it in as multiple parameters
throttle
Throttling the specified function
const throttle = (fn, time) => {
let timer;
return (...args) => {
if (!timer) {
timer = setTimeout(() => {
fn(...args);
timer = null;
}, time);
}
};
};
// Avoid excessive updating of positioning during scrolling
jQuery(window).on('scroll', throttle(updatePosition, 100));
// `renewToken` is called after clicking, but more than once within 5 minutes.
const throttled = throttle(renewToken, 300000, { trailing: false });
jQuery(element).on('click', throttled);
// Cancel a trailing throttling call.
jQuery(window).on('popstate', throttled.cancel);
Idea: Return a new function that throttles the original function
Object
assign
Merge the properties of objects, and the properties of subsequent objects will overwrite the properties of previous objects.
const assign = (...objs) =>
objs.reduce((result, obj) => Object.assign(result, obj), {});
function Foo() {
this.a = 1;
}
function Bar() {
this.c = 3;
}
Foo.prototype.b = 2;
Bar.prototype.d = 4;
assign({ a: 0 }, new Foo(), new Bar());
// => { 'a': 1, 'c': 3 }
Idea: Use the reduce method to traverse each object and merge the attributes into the target object.
defaults
Encapsulate the specified object and merge the default value into it
const defaults = (obj, defaultProps) => ({ ...defaultProps, ...obj });
defaults({ a: 1 }, { b: 2 }, { a: 3 });
// => { 'a': 1, 'b': 2 }
Idea: Use the Object.assign method to merge the default value object into the target object. If the same attribute already exists in the target object, it will not be overwritten.
defaultsDeep
Similar to defaults, but supports nested objects
const defaultsDeep = (obj, defaultProps) => {
const mergeDeep = (target, source) => {
Object.keys(source).forEach(key => {
const targetValue = target[key];
const sourceValue = source[key];
if (typeof targetValue === 'object' && typeof sourceValue === 'object') {
target[key] = mergeDeep(targetValue, sourceValue);
} else {
target[key] = targetValue === undefined ? sourceValue : targetValue;
}
});
return target;
};
return mergeDeep({ ...defaultProps }, obj);
};
defaultsDeep({ a: { b: 2 } }, { a: { b: 1, c: 3 } });
// => { 'a': { 'b': 2, 'c': 3 } }
Idea: Use the Object.assign and typeof methods to perform recursive traversal and merge nested objects into them.
findKey
Traverse the object and return the first key name that meets the conditions
const findKey = (obj, predicate) => {
for (const key in obj) {
if (predicate(obj[key], key, obj)) {
return key;
}
}
};
const users = {
barney: { age: 36, active: true },
fred: { age: 40, active: false },
pebbles: { age: 1, active: true },
};
findKey(users, o => o.age < 40);
// => 'barney' (iteration order is not guaranteed)
// The `matches` iteratee shorthand.
findKey(users, { age: 1, active: true });
// => 'pebbles'
// The `matchesProperty` iteratee shorthand.
findKey(users, ['active', false]);
// => 'fred'
// The `property` iteratee shorthand.
findKey(users, 'active');
// => 'barney'
Idea: Use for...in to loop through the object, call the specified function for each attribute to judge, and return the current attribute name if a true value is returned.
findLastKey
Similar to findKey, but starts at the end of the object
const findLastKey = (obj, predicate) =>
findKey(obj, predicate, Object.keys(obj).reverse());
const users = {
barney: { age: 36, active: true },
fred: { age: 40, active: false },
pebbles: { age: 1, active: true },
};
findLastKey(users, o => o.age < 40);
// => returns 'pebbles' assuming `findKey` returns 'barney'
// The `matches` iteratee shorthand.
findLastKey(users, { age: 36, active: true });
// => 'barney'
// The `matchesProperty` iteratee shorthand.
findLastKey(users, ['active', false]);
// => 'fred'
// The `property` iteratee shorthand.
findLastKey(users, 'active');
// => 'pebbles'
Idea: Use the Object.keys method to get the key array of the object, then use the reverse method to flip the array, and then use the findKey function to search.
forIn
Traverse the object and call the specified function for each attribute
const forIn = (obj, iteratee) => {
for (const key in obj) {
if (iteratee(obj[key], key, obj) === false) {
break;
}
}
return obj;
};
function Foo() {
this.a = 1;
this.b = 2;
}
Foo.prototype.c = 3;
forIn(new Foo(), (value, key) => {
console.log(key);
});
// => Logs 'a', 'b', then 'c' (the order of traversal cannot be guaranteed).
Idea: Use for...in to loop through the object and call the specified function for each attribute.
forInRight
Similar to forIn, but starts traversing from the end of the object
const forInRight = (obj, fn) => {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
fn(obj[key], key, obj);
}
}
};
function Foo() {
this.a = 1;
this.b = 2;
}
Foo.prototype.c = 3;
forInRight(new Foo(), (value, key) => {
console.log(key);
});
// => print 'c', 'b', then 'a', `forIn` will print 'a', 'b', then 'c'.
Idea: Use a for...in loop to traverse all the properties of the object in reverse order and call the specified function for each property.
forOwn
Traverse the enumerable properties of the object itself and call the specified function for each property
const forOwn = (obj, func) => {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
func(obj[key], key, obj);
}
}
};
function Foo() {
this.a = 1;
this.b = 2;
}
Foo.prototype.c = 3;
forOwn(new Foo(), (value, key) => {
console.log(key);
});
// => Output 'a' then 'b' (the order of traversal is not guaranteed).
Idea: Traverse the enumerable properties of the object itself, call the specified function for each property, use a for-in loop to traverse all the properties of the object, determine whether the property is its own enumerable property, and if so, call the specified function.
forOwnRight
Similar to forOwn, but starts traversing from the end of the object
const forOwnRight = (obj, func) => {
const keys = Object.keys(obj).reverse();
for (let i = 0; i < keys.length; i++) {
func(obj[keys[i]], keys[i], obj);
}
};
function Foo() {
this.a = 1;
this.b = 2;
}
Foo.prototype.c = 3;
forOwnRight(new Foo(), (value, key) => {
console.log(key);
});
// => print 'b' then 'a', `forOwn` will print 'a' then 'b'
Idea: Similar to forOwn, but starting from the end of the object, you can reverse the key array of the object and then traverse.
functions
Returns all function names on the specified object
const functions = obj =>
Object.keys(obj).filter(key => typeof obj[key] === 'function');
function Foo() {
this.a = constant('a');
this.b = constant('b');
}
Foo.prototype.c = constant('c');
functions(new Foo());
// => ['a', 'b']
Idea: Return all function names on the specified object, use Object.keys() to obtain all attribute names of the object, and then use the filter() method to filter out attribute names whose attribute values are of type function.
get
Get the attributes on the object and support using dots and square brackets to specify the attribute path.
const get = (obj, path) =>
path.split(/[.[\]]/).reduce((acc, cur) => (cur ? acc[cur] : acc), obj);
const object = { a: [{ b: { c: 3 } }] };
get(object, 'a[0].b.c');
// => 3
get(object, ['a', '0', 'b', 'c']);
// => 3
get(object, 'a.b.c', 'default');
// => 'default'
Idea: Use the reduce function to split the attribute path and then traverse and obtain the corresponding attribute value. It supports specifying the attribute path using dots and square brackets.
has
Determine whether there is a specified attribute on the object
const has = (obj, key) => key in obj;
const object = { a: { b: 2 } };
const other = create({ a: create({ b: 2 }) });
has(object, 'a');
// => true
has(object, 'a.b');
// => true
has(object, ['a', 'b']);
// => true
has(other, 'a');
// => false
Idea: Use the in operator to determine whether there is a specified attribute on the object
hasIn
Determine whether the object has an attribute with the specified path
const hasIn = (obj, path) => get(obj, path) !== undefined;
const object = create({ a: create({ b: 2 }) });
hasIn(object, 'a');
// => true
hasIn(object, 'a.b');
// => true
hasIn(object, ['a', 'b']);
// => true
hasIn(object, 'b');
// => false
Idea: Use the get function to obtain the attribute value. If undefined is returned, it means that the specified path attribute does not exist.
invert
Reverse the properties and values of the specified object
const invert = obj =>
Object.entries(obj).reduce((acc, [key, val]) => {
acc[val] = key;
return acc;
}, {});
const object = { a: 1, b: 2, c: 1 };
invert(object);
// => { '1': 'c', '2': 'b' }
Idea: Traverse the object and use the attribute value as the key name, and the attribute name as the key value to generate a new object
invertBy
Similar to invert, but supports specifying a collection of inverted values
const invertBy = (obj, fn) =>
Object.entries(obj).reduce((acc, [key, val]) => {
const invertedKey = fn(val);
if (!acc[invertedKey]) {
acc[invertedKey] = [];
}
acc[invertedKey].push(key);
return acc;
}, {});
const object = { a: 1, b: 2, c: 1 };
invertBy(object);
// => { '1': ['a', 'c'], '2': ['b'] }
invertBy(object, value => `group${value}`);
// => { 'group1': ['a', 'c'], 'group2': ['b'] }
Idea: Traverse the object and process the attribute value through the callback function as the key name, and use the attribute name as the key value to generate a new object
invoke
Call a method on the specified object
const invoke = (obj, methodName, ...args) =>
Object.values(obj).forEach(func =>
typeof func[methodName] === 'function' ? func[methodName](...args) : null
);
const object = { a: [{ b: { c: [1, 2, 3, 4] } }] };
invoke(object, 'a[0].b.c.slice', 1, 3);
// => [2, 3]
Idea: Traverse the object and call the method with the specified method name
keys
Returns all enumerable property names on the object
const keys = obj => Object.keys(obj);
function Foo() {
this.a = 1;
this.b = 2;
}
Foo.prototype.c = 3;
keys(new Foo());
// => ['a', 'b'] (iteration order is not guaranteed)
keys('hi');
// => ['0', '1']
Idea: Use the Object.keys function to return all enumerable property names on the object
keysIn
Returns all property names on the object, including non-enumerable properties
const keysIn = obj => {
const result = [];
for (const key in obj) {
result.push(key);
}
return result;
};
function Foo() {
this.a = 1;
this.b = 2;
}
Foo.prototype.c = 3;
keysIn(new Foo());
// => ['a', 'b', 'c'] (iteration order is not guaranteed)
Idea: Traverse all property names of the object, add them to an array, and return the array.
mapKeys
Iterates over each property on the object, returning a new object where the name of each property is calculated by the specified function
const mapKeys = (obj, fn) =>
Object.keys(obj).reduce((result, key) => {
result[fn(obj[key], key, obj)] = obj[key];
return result;
}, {});
mapKeys({ a: 1, b: 2 }, (value, key) => key + value);
// => { 'a1': 1, 'b2': 2 }
Idea: Use reduce to traverse the attribute names of the object, calculate the new attribute name through the specified function, add it to a new object together with the original attribute value, and return the new object.
mapValues
Iterates over each property on the object, returning a new object in which the value of each property is calculated by the specified function
const mapValues = (obj, fn) =>
Object.keys(obj).reduce((result, key) => {
result[key] = fn(obj[key], key, obj);
return result;
}, {});
const users = {
fred: { user: 'fred', age: 40 },
pebbles: { user: 'pebbles', age: 1 },
};
mapValues(users, o => o.age);
// => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
// The `property` iteratee shorthand.
mapValues(users, 'age');
// => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
Idea: Use reduce to traverse the attribute names of the object, calculate each attribute value through the specified function, add the calculated new attribute value to a new object, and return the new object.
merge
Merges the properties of the object and source object and returns the merged object
const merge = (obj, src) => ({ ...obj, ...src });
const object = {
a: [{ b: 2 }, { d: 4 }],
};
const other = {
a: [{ c: 3 }, { e: 5 }],
};
merge(object, other);
// => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
Idea: Use Object.assign to merge the attribute values of the source object into the target object, and return the merged new object.
mergeWith
Similar to merge, but specifies a merge function to handle conflicting attribute values
const mergeWith = (obj, src, customizer) => {
const result = { ...obj, ...src };
Object.keys(result).forEach(key => {
result[key] = customizer(obj[key], src[key], key, obj, src);
});
return result;
};
function customizer(objValue, srcValue) {
if (isArray(objValue)) {
return objValue.concat(srcValue);
}
}
const object = { a: [1], b: [2] };
const other = { a: [3], b: [4] };
mergeWith(object, other, customizer);
// => { 'a': [1, 3], 'b': [2, 4] }
Idea: Use Object.assign to merge the attribute values of the source object into the target object, traverse the merged new object, customize the conflicting attribute values by specifying a function, and return the processed new object.
omit
Returns a new object with the property values of the specified properties omitted
const omit = (obj, props) => {
const newObj = { ...obj };
props.forEach(prop => {
delete newObj[prop];
});
return newObj;
};
const object = { a: 1, b: '2', c: 3 };
omit(object, ['a', 'c']);
// => { 'b': '2' }
Idea: Use Object.assign to copy the attribute values of the original object to a new object, traverse the specified omitted attributes, delete them from the new object, and return the new object.
omitBy
Similar to omit, but determines whether to omit attributes based on the specified function
const omitBy = (obj, predicate) => {
const newObj = { ...obj };
Object.keys(newObj).forEach(key => {
if (predicate(newObj[key])) {
delete newObj[key];
}
});
return newObj;
};
const object = { a: 1, b: '2', c: 3 };
omitBy(object, isNumber);
// => { 'b': '2' }
Idea: Use Object.assign to copy the attribute values of the original object to a new object, traverse each attribute of the new object, determine whether the attribute needs to be deleted according to the specified function, and return the processed new object.
pick
Returns a new object containing only the property values of the specified property
const pick = (obj, props) =>
props.reduce((result, prop) => {
if (prop in obj) {
result[prop] = obj[prop];
}
return result;
}, {});
const object = { a: 1, b: '2', c: 3 };
pick(object, ['a', 'c']);
// => { 'a': 1, 'c': 3 }
Idea: Use reduce to traverse the specified attributes that need to be selected, add them to a new object, and return the new object.
pickBy
Similar to pick, but determines whether to retain attributes based on the specified function
const pickBy = (obj, fn) =>
Object.keys(obj).reduce((acc, key) => {
if (fn(obj[key])) acc[key] = obj[key];
return acc;
}, {});
const object = { a: 1, b: '2', c: 3 };
pickBy(object, isNumber);
// => { 'a': 1, 'c': 3 }
Idea: Use the Object.keys and Array.prototype.reduce methods to return a new object.
result
Get the value of the specified path on the object and make function calls according to the situation
const result = (obj, path, defaultValue) =>
path.split('.').reduce((acc, cur) => (acc ? acc[cur] : undefined), obj) ??
defaultValue;
const object = { a: [{ b: { c1: 3, c2: constant(4) } }] };
result(object, 'a[0].b.c1');
// => 3
result(object, 'a[0].b.c2');
// => 4
result(object, 'a[0].b.c3', 'default');
// => 'default'
result(object, 'a[0].b.c3', constant('default'));
// => 'default'
Idea: Use the Array.prototype.reduce method and typeof operator to support obtaining the value of multi-layer paths.
set
Sets the property value of the specified path on the object
const set = (obj, path, value) => {
const keys = path.split(/[,[\].]+?/);
const lastKeyIndex = keys.length - 1;
keys.reduce((acc, key, index) => {
if (index === lastKeyIndex) acc[key] = value;
else acc[key] ?? (acc[key] = {});
return acc[key];
}, obj);
return obj;
};
const object = { a: [{ b: { c: 3 } }] };
set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4
set(object, ['x', '0', 'y', 'z'], 5);
console.log(object.x[0].y.z);
// => 5
Idea: Use the Array.prototype.reduce method to support setting the value of multi-layer paths.
setWith
Similar to set, but specifies a custom function to use to set the property value
const setWith = (obj, path, value, customizer) => {
const keys = path.split(/[,[\].]+?/);
const lastKeyIndex = keys.length - 1;
keys.reduce((acc, key, index) => {
const newValue = index === lastKeyIndex ? customizer(acc[key], value) : {};
acc[key] = typeof acc[key] === 'object' ? acc[key] : newValue;
return acc[key];
}, obj);
return obj;
};
const object = {};
setWith(object, '[0][1]', 'a', Object);
// => { '0': { '1': 'a' } }
Idea: Use the Array.prototype.reduce method to support setting the value of multi-layer paths.
toPairs
Convert object to array of key-value pairs
const toPairs = obj => Object.entries(obj);
function Foo() {
this.a = 1;
this.b = 2;
}
Foo.prototype.c = 3;
toPairs(new Foo());
// => [['a', 1], ['b', 2]] (iteration order is not guaranteed)
Idea: Use the Object.entries method to return an array composed of key-value pairs.
toPairsIn
Convert an object into an array of key-value pairs, including non-enumerable properties
const toPairsIn = obj => {
const result = [];
for (const key in obj) {
result.push([key, obj[key]]);
}
return result;
};
function Foo() {
this.a = 1;
this.b = 2;
}
Foo.prototype.c = 3;
toPairsIn(new Foo());
// => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed)
Idea: Use the Object.getOwnPropertyNames method to return an array composed of key-value pairs.
transform
Encapsulate the specified object, specify the conversion function, and process the attributes on the object
const transform = (obj, fn, acc) =>
Object.entries(obj).reduce(
(result, [key, value]) => fn(result, value, key, obj),
acc
);
transform(
[2, 3, 4],
(result, n) => {
result.push((n *= n));
return n % 2 == 0;
},
[]
);
// => [4, 9]
transform(
{ a: 1, b: 2, c: 1 },
(result, value, key) => {
(result[value] || (result[value] = [])).push(key);
},
{}
);
// => { '1': ['a', 'c'], '2': ['b'] }
Idea: Use the Object.entries method and the Array.prototype.reduce method to return an array composed of converted objects.
unset
Delete the attribute value of the specified path on the object
Idea:
update: Get the value of the specified path on the object, make function calls according to the situation, and finally set the value back
Idea:
updateWith: similar to update, but specifies a custom function to update attribute values
Idea:
values: Returns all enumerable property values on the object
Idea:
valuesIn: Returns all attribute values on the object, including non-enumerable attribute values
Idea:
Idea:
gather
countBy: Traverse the collection and return an object, where the key is the value calculated by the specified function, and the value is the number of times the value appears.
each: Traverse the collection and call the specified function on each element
Idea:
eachRight: Similar to each, but starts traversing from the end of the collection
Idea:
every: Traverse the collection and return a Boolean value indicating whether all elements satisfy the specified function
Idea:
filter: Traverse the collection and return a new collection that only contains elements that satisfy the specified function
Idea:
find: Traverse the collection and return the first element that satisfies the specified function
Idea:
findLast: Similar to find, but starts traversing from the end of the collection
Idea:
flatMap: Traverse the collection, call the specified function on each element, and flatten the result into a new collection
Idea:
flatMapDeep: Similar to flatMap, but the result of calling the iterator on each element is flattened deeply.
Idea:
flatMapDepth: similar to flatMap, but you can specify the flattening depth
Idea:
forEach: Traverse the collection and call the specified function on each element
Idea:
forEachRight: Similar to forEach, but starts traversing from the end of the collection
Idea:
groupBy: Traverse the collection and return an object, where the key is the value calculated by the specified function, and the value is the set of elements corresponding to the value.
Idea:
includes: Determine whether the specified element is included in the collection
Idea:
invokeMap: Call the specified method on each element in the collection and return the result
Idea:
keyBy: Traverse the collection and return an object in which the key is calculated by the specified function and the value is the element
Idea:
map: Traverse the collection and return a new collection in which each element is calculated by the specified function
Idea:
orderBy: Sort the collection in the specified order
Idea:
partition: Traverse the collection and return an array containing a collection of elements that satisfy and do not satisfy the specified function.
Idea:
reduce: Traverse the collection, accumulate the collection elements, and return the final result
Idea:
reduceRight: Similar to reduce, but starts traversing from the end of the collection
Idea:
reject: Traverse the collection and return a new collection that does not contain elements that satisfy the specified function
Idea:
sample: Returns a random element in the collection
Idea:
sampleSize: Returns a specified number of random elements in the collection
Idea:
shuffle: Returns a randomly arranged collection
Idea:
size: Returns the size of the collection
Idea:
some: Traverse the collection and return a Boolean value indicating whether any element satisfies the specified function
Idea:
sortBy: Traverse the collection, call the specified function on each element in the specified order, and return the result
Idea:
toArray: Convert the collection into an array
Idea:
toPairs: Convert the collection into an array of key-value pairs
Idea:
union: merge multiple collections into one collection and remove duplicates at the same time
Idea:
uniq: Returns a new collection that does not contain duplicate elements
Idea:
uniqBy: Similar to uniq, but deduplicates according to the specified function
Idea:
uniqWith: Similar to uniq, but uses the specified comparison function for deduplication
Idea:
unzip: Return the grouped elements to the structure before packaging
Idea:
unzipWith: Similar to unzip, but uses the specified function to merge each group element
Idea:
without: Returns a new collection that does not contain the specified element
Idea:
xor: Returns a new set containing elements that appear in only one of the sets
Idea:
zip: Merge multiple collections into a new collection of elements. Each element is composed of elements at the corresponding position of each original collection.
Idea:
zipObject: Combine key array and value array into a new object
Idea:
zipWith: Similar to zip, but uses the specified function to merge each group element
Idea:
Ramda
A practical JavaScript functional programming library
Ramda function list
JavaScript Advanced Programming 4th Edition Study Notes
Common React interview questions
React interview questions
| Serial Number | Question |
| -- | -------------------------------------------------- -------------------------------- |
| 1 | What does the key attribute do? |
| 2 | What does the refs attribute do? |
| 3 | What does the PureComponent component do? |
| 4 | What does the memo method do? |
| 5 | What does the error boundary do? |
| 6 | What are controlled components and uncontrolled components? |
| 7 | What are higher-order components? |
| 8 | What are the life cycle methods and what is their execution order? |
| 9 | What does the getDerivedStateFromProps life cycle method do? |
| 10 | What does the shouldComponentUpdate life cycle method do? |
| 11 | What does the getSnapshotBeforeUpdate life cycle method do? |
| 12 | What is React context? |
| 13 | What is useState in React Hook? |
- ### What is the function of key attribute?
The key
attribute is used to identify each child element in the list, allowing for more efficient updates to the DOM when elements are added, moved, or removed. The key
attribute should be a unique string, preferably generated based on the unique identifier of the element in the list.
function App() {
const items = [
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
];
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
-
What is the role of the refs attribute?
The
refs
attribute is used to reference components or DOM elements. It can be used in function components and class components.
function App() {
const buttonRef = useRef();
function handleClick() {
buttonRef.current.disabled = true;
}
return (
<>
<button ref={buttonRef} onClick={handleClick}>
Click me
</button>
</>
);
}
-
What does the PureComponent component do?
PureComponent
is an optimized version based on Component that uses shallow comparison in the shouldComponentUpdate life cycle method to determine whether re-rendering is needed.PureComponent
will not re-render if all props and state have not changed.
class MyComponent extends React.PureComponent {
render() {
return <div>Hello, {this.props.name}!</div>;
}
}
- ### What does the memo method do?
The memo
method is a higher-order component used for shallow comparison optimization of function components. It takes a function component and returns a new component that will use the previous result if the props haven't changed.
function MyComponent(props) {
return <div>Hello, {props.name}!</div>;
}
const MemoizedComponent = React.memo(MyComponent);
- ### What is the role of error boundaries?
ErrorBoundary is a React component used to catch and handle JavaScript errors in child components. Error boundaries catch errors that occur during rendering, but not errors in event handlers, asynchronous code, and server-side rendering.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
console.error(error, info);
}
render() {
if (this.state.hasError) {
return <div>Something went wrong.</div>;
}
return this.props.children;
}
function App() {
return (
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
);
}
-
What are controlled components and uncontrolled components?
In React, form elements such as input, textarea, and select are usually divided into controlled and uncontrolled components.
Controlled components
means that the value of a form element is controlled by the state of a React component. As the user enters content, React updates the component's state, thus updating the form element's value in real time. At this point, the values of the form elements are maintained by React and have nothing to do with the DOM itself. Controlled components typically need to implement an onChange event handler to update the component's state as the user enters content.
Here is a sample code using a controlled component:
class Input extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
};
}
handleChange = event => {
this.setState({
value: event.target.value,
});
};
render() {
return (
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
);
}
}
In the above code, we define an Input component as a controlled component, and its value is controlled by the component's state value. When the user enters content, the onChange event handler function handleChange will be called to update the state of the component, thereby updating the value of the form element in real time.
In contrast, an uncontrolled component
means that the form element's value is maintained by the DOM itself and is not controlled by the React component. When the user enters content, the value of the form element is updated directly to the DOM without any processing in React. At this time, the component cannot directly obtain the value of the form element, but needs to obtain it through ref.
Here is a sample code using an uncontrolled component:
class Input extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
handleClick = () => {
console.log(this.inputRef.current.value);
};
render() {
return (
<div>
<input type="text" ref={this.inputRef} />
<button onClick={this.handleClick}>Click</button>
</div>
);
}
}
In the above code, we have defined an Input component as an uncontrolled component whose value is maintained by the DOM itself. When the user clicks the button, we can get the value of the form element through ref for subsequent processing.
-
What are higher-order components?
Higher-Order Component
(HOC for short) refers to a function that accepts a component as a parameter and returns a new component. HOC is essentially a way of component reuse, used to enhance the functionality of components or encapsulate some common logic to achieve code reuse.
Here is a sample code using higher-order components:
function withLogger(WrappedComponent) {
return class extends React.Component {
componentDidMount() {
console.log(`Component ${WrappedComponent.name} mounted`);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
}
class MyComponent extends React.Component {
render() {
return <div>Hello, world!</div>;
}
}
const EnhancedComponent = withLogger(MyComponent);
ReactDOM.render(<EnhancedComponent />, document.getElementById('root'));
In the above code, we define a higher-order component withLogger, which accepts a component as a parameter and returns a new component. The new component will output the component name when mounted, and then render the incoming component.
We defined a MyComponent component and enhanced it using the withLogger higher-order component to get a new enhanced component EnhancedComponent. Finally, we render the EnhancedComponent into the DOM.
In the above example, the withLogger high-order component is used to record the mounting information of the component to make it more convenient during development and debugging. When we need to perform similar operations on multiple components, we can use the withLogger higher-order component without writing the same mounting logic in each component. In this way, we can achieve code reuse while making the code more concise and easy to understand.
-
What are the life cycle methods and what is their execution order?
Lifecycle methods in React can be divided into three categories: mount, update, and unmount. Their execution order is as follows:
- Mount:
- constructor
- getDerivedStateFromProps *render
- componentDidMount
- renew:
- getDerivedStateFromProps
- shouldComponentUpdate *render
- getSnapshotBeforeUpdate
- componentDidUpdate
- uninstall:
- componentWillUnmount
Among them, the getDerivedStateFromProps
life cycle method is a static method used to update the state of the component when props change. It should return an object to update the state, or null to indicate no update is required.
class MyComponent extends React.Component {
static getDerivedStateFromProps(props, state) {
if (props.value !== state.value) {
return { value: props.value };
}
return null;
}
constructor(props) {
super(props);
this.state = { value: props.value };
}
render() {
return <div>{this.state.value}</div>;
}
}
Secondly, the shouldComponentUpdate
lifecycle method is used to decide whether the component needs to be re-rendered when props or state change. It should return a boolean value indicating whether the component needs to be updated. By default, shouldComponentUpdate
returns true.
class MyComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return this.props.value !== nextProps.value;
}
render() {
return <div>{this.props.value}</div>;
}
}
Finally, the getSnapshotBeforeUpdate
lifecycle method is called before the component is updated, which can be used to capture some information before the DOM is updated. It should return a value as the third parameter of the componentDidUpdate method.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
if (prevProps.items.length < this.props.items.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}>
{this.props.items.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
}
- ### What does the getDerivedStateFromProps life cycle method do?
getDerivedStateFromProps
is a static lifecycle method of React components.
Its main function is to allow the component to synchronously update the component's state when it receives new props.
This method will be called by React in the following two situations:
After component instantiation and before rendering
Before receiving new props
, whether caused by the parent component or the props
changes obtained through the external API
The getDerivedStateFromProps
method receives two parameters:
props
: latest props
state
: current state
This method should return an object to update state
, or null
to not update any state.
class MyComponent extends React.Component {
static getDerivedStateFromProps(props, state) {
// You can update state based on changes in props
if (props.value !== state.value) {
return {
value: props.value,
};
}
// Return null and do not update state
return null;
}
}
Key points
:
getDerivedStateFromProps
is a pure function and should not have side effects, such as making network requests or subscriptions.
This method is not recommended for frequent use as it may cause the code to become complex and difficult to maintain.
In many scenarios, you can use other lifecycle methods or React's newly introduced Hooks
.
When changes in props need to be mapped to state
, you can consider using it, but in many cases, the rendering content can be calculated directly from props
without using state
.
This method was introduced since React v16.3
and componentWillReceiveProps
has been deprecated because it is safer and will not be affected by future asynchronous rendering features. In future versions of React, the use of class-based lifecycle methods will gradually give way to functional components using Hooks
.
- ### What does the shouldComponentUpdate life cycle method do?
shouldComponentUpdate
is a life cycle method of React class components. Its main function is to determine whether the output of a component needs to be updated. That is, when the props
or state
of the component changes, the shouldComponentUpdate
method will be called before rendering is executed. Called to indicate to React whether it should continue the rendering process.
This method returns true
by default, which means that the component will be re-rendered every time the state changes. However, by returning false
, you can prevent unnecessary rendering of components, which can improve the performance of your application, especially when the component tree is very large or computationally intensive.
shouldComponentUpdate
receives two parameters:
nextProps
: new props
to be received
nextState
: the new state
to be updated
class MyComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
// Compare the old and new props or state and only update the component when they actually change
return (
nextProps.someValue !== this.props.someValue ||
nextState.someState !== this.state.someState
);
}
}
Note
:
It is only called during component update and will not be called during first rendering.
If false
is returned, the component will not perform an update operation, the render
method will not be called, and the rendering of child components will also be skipped.
It should not produce any side effects and should be a pure function.
In most cases, you don't need to write the shouldComponentUpdate
method manually. If you need to optimize performance, it is recommended to use React.PureComponent
, which already implements a shallow comparison similar to shouldComponentUpdate
.
Starting with React 16.3
version, a new "lifecycle" API has been introduced. If you are using the new lifecycle methods, or plan to migrate to functional components and Hooks
, shouldComponentUpdate
may become less common
In particular, React.memo
works similar to PureComponent for functional components, providing similar performance improvements.
- ### What does the getSnapshotBeforeUpdate life cycle method do?
getSnapshotBeforeUpdate
is a lifecycle method in React class components that allows you to capture certain information about the component (for example, the scroll position) before the latest rendered output is submitted to the DOM
.
This lifecycle method is called before new rendering output is drawn, and it can return a value or null
. If the returned value is not null
, the returned value will be passed as the third parameter to componentDidUpdate
.
This mechanism is particularly useful because sometimes after updating the DOM
you may need to adjust the scroll position based on the previous state, or perform similar operations to keep the user's view state unchanged.
getSnapshotBeforeUpdate
receives two parameters:
prevProps
: props
before update
prevState
: state
before update
If your component doesn't use getSnapshotBeforeUpdate, you don't need to implement it; only use it if you really need to capture some information before updating and apply it after updating.
class MyComponent extends React.Component {
getSnapshotBeforeUpdate(prevProps, prevState) {
// Check whether prevProps or prevState meets specific conditions
// For example, you can capture the old scroll position:
if (prevProps.list.length < this.props.list.length) {
const list = document.getElementById('list');
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// If the `snapshot` returned by `getSnapshotBeforeUpdate` is not `null`
// You can do some things with `snapshot`
if (snapshot !== null) {
const list = document.getElementById('list');
list.scrollTop = list.scrollHeight - snapshot;
}
}
}
In the above example, getSnapshotBeforeUpdate
is used to capture the scroll position before adding a new item to the list, and then use this snapshot information (snapshot
) through componentDidUpdate
to adjust the scroll bar to maintain the scroll position relative to the bottom. Does not change even if the list length changes.
It should be noted that getSnapshotBeforeUpdate
and componentDidUpdate
, when used together, can handle operations that need to be performed immediately after the DOM is updated.
-
What is React context?
React context is a way to share data across component levels, avoiding the trouble of passing data through props. It consists of two parts:
Provider
andConsumer
.
Provider
is a component that accepts a value attribute, which represents shared data. The Consumer
of the subcomponent that wraps it can access this data.
const MyContext = React.createContext();
class MyComponent extends React.Component {
render() {
return (
<MyContext.Provider value={42}>
<ChildComponent />
</MyContext.Provider>
);
}
}
function ChildComponent() {
return (
<MyContext.Consumer>
{value => <div>The answer is {value}.</div>}
</MyContext.Consumer>
);
}
- ### What is useState in React Hook?
useState is a type of React Hook that allows us to use state in functional components.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
๐ Hydrology in previous issues
Implement a react component that automatically calculates width and height based on ResizeObserver
Top comments (0)