In the process of writing and maintaining front-end code, we often encounter scenarios where performance optimization is needed. Especially when dealing with arrays and objects, using the appropriate methods provided by JavaScript can not only improve the execution efficiency of the code, but also make the code more concise and understandable. In this article, we will explore how to optimize the code by using JavaScript's array and collection methods through a review of actual business code.
Business Scenario#
Let's start with a common scenario where we need to verify if the user has assigned the corresponding permissions for each selected permission scope. This requirement sounds straightforward, but in the actual implementation, it may involve a series of array operations. Here is the source code we want to optimize:
// Initial permission validation logic
const selectedObjItem = [];
this.selectedPermissions.forEach((item) => {
!selectedObjItem.includes(item.action_id) && selectedObjItem.push(item.action_id);
});
const result = this.selectedScopes.every(scope => {
if (scope.has_instance) {
return selectedObjItem.includes(scope.action_id);
}
return true;
})
if (!result) {
showMsg(this.$t('AUTH:请选择对象范围'), 'warning');
return false;
}
The purpose of this code is to validate whether each selected scenario (selectedScopes
) has selected a permission scope, and these permission scopes need to be found in selectedPermissions
(each permission object has a unique identifier action_id
).
Although the code seems to achieve the desired effect, upon careful analysis, we will find that there is room for optimization. In fact, we can improve the execution efficiency and make the code clearer by making a few simple changes. To do this, consider the following questions:
- Is there a more efficient way to achieve array recording and deduplication?
- Is
every
the best method for iterating and validating selections? - Does the final result judgment have to wait until all iterations are completed?
Optimization#
1. Deduplication and Recording#
The first step of recording actually includes deduplication, mainly determined by the evidence judgment before push
: !selectedObjItem.includes(item.action_id)
. Whenever deduplication is involved, we can think of using the efficient data type Set
provided by ES6, because Set
itself has the property of deduplication. Therefore, the code can be optimized as follows:
const selectedObjItem = new Set();
this.selectedPermissions.forEach((item) => {
selectedObjItem.add(item.action_id);
});
Using Set
for deduplication and checking the existence of elements with the has
method is usually more efficient than using an array for deduplication (using push
and includes
to check). The reasons are as follows:
- Time Complexity:
- The time complexity of the
add
andhas
methods ofSet
is usually constant time complexity O(1). This is becauseSet
is implemented using a hash table internally, which allows for fast lookup and insertion of data. - The
includes
method of an array has a time complexity of O(n) because in the worst case, it needs to traverse the entire array to determine if an element exists.
- The time complexity of the
- Deduplication:
- The
Set
data structure is designed to be a collection of unique elements, and it automatically manages the uniqueness of elements. When you try to add an element that already exists in theSet
, it will not perform any operations, so there is no need to manually write deduplication logic. - In the case of deduplication in an array, additional logic must be written (usually using
includes
to check and thenpush
) to ensure that the added elements are unique, which increases code complexity and the number of operations performed.
- The
- Code Simplicity:
- Using
Set
makes the code more concise and easier to understand. Because the interface ofSet
is clear and represents the data structure of a set, it makes the code's intention clearer and more semantically meaningful. - Using array deduplication involves the use of
includes
andpush
methods, although they are both array methods and easy to understand, they need to be used together to achieve deduplication, which makes the code less intuitive.
- Using
The use and application of Set
will be introduced in the following section.
2. Optimization of Validation#
Using the every
method in the source code is acceptable, but it is also inefficient because every
needs to iterate through all items. However, the purpose of this validation is to ensure that all items are selected, so if one item is not selected, we can return the error result early without checking the remaining items.
With this idea in mind, it is easy to think of the some
method. The optimized code is as follows:
const result = this.selectedScopes.some(scope => {
if (scope.has_instance) {
return !selectedObjItem.has(scope.action_id);
}
return false;
});
The code logic and structure look the same as before, but the efficiency is improved because some
stops iterating after returning true
.
3. Complete Code#
The complete optimized code is as follows:
// Deduplicate and record action_id
const selectedObjItem = new Set();
this.selectedPermissions.forEach((item) => {
selectedObjItem.add(item.action_id);
});
// Check if all options are selected
const result = this.selectedScopes.some(scope => {
if (scope.has_instance) {
return !selectedObjItem.has(scope.action_id);
}
return false;
});
if (result) {
showMsg(this.$t('AUTH:请选择对象范围'), 'warning');
return false;
}
Scenario Training#
Similarly, consider the following example:
Given batchAddSource
is a two-dimensional array with values: [['a','b'], ['c','d'], ...]
; selectedList
is a subset of batchAddSource
, representing the selected content. Now we need a method getUnselectedList
that takes the selected content as input and returns the unselected content.
Approach#
The core of this problem is still deduplication. Combining the optimization ideas mentioned earlier, we can have the following thoughts:
- For comparing two-dimensional arrays, we can convert the inner arrays into a more suitable string type for comparison (
join
method). - Use the
has
method of theSet
data structure to check if an element exists.
const getUnselectedList = (selectedList, batchAddSource) => {
const selectedSource = new Set(selectedList.map(item => item.join('.')))
return batchAddSource.filter(item => !selectedSource.has(item.join('.')))
}
Conclusion#
JavaScript is a constantly evolving language. The introduction of new syntax and features in ES6 greatly improves the efficiency and readability of code writing. Here is a summary of the methods and applications mentioned:
- Using the new data structure
Set
: It is very convenient to achieve array deduplication and efficient recording of unique values usingSet
. Compared to traditional traversal and checking methods, the native mechanism ofSet
ensures the uniqueness of elements in the set, which plays a significant role in performance optimization, especially when dealing with large amounts of data. - Using array methods wisely: ES6 provides array methods such as
forEach
,every
, andsome
. Using these methods reasonably can make the code clearer and choose the most suitable iteration method according to different business logic requirements. For example, usingsome
to return results early to avoid unnecessary iteration. - Optimizing logical judgments: By optimizing the logic, such as early exit from loops, unnecessary calculations can be avoided, saving resources and time. When implementing validation operations on arrays or collections, considering boundary conditions and short-circuit logic can achieve more efficient code execution.
- Utilizing the concise syntax of ES6: New features such as arrow functions and template strings can make the code more concise, avoid redundant code, and improve code readability.
In practical programming practice, although each optimization point may only bring a small performance improvement, overall, these improvements can significantly improve the performance and user experience of the application. By combining the above optimization methods, we can write JavaScript code that is both efficient and highly readable.
Appendix: Explanation and Examples of Set#
Set
is a special type of collection - a "set of values" (without keys), where each value can only occur once.
The previous section has introduced an application scenario of Set
. Here, let's review the characteristics of this data type. Its main methods are as follows:
new Set(iterable)
- Creates aset
, and if aniterable
object (usually an array) is provided, it will copy the values from the array into theset
.set.add(value)
- Adds a value and returns theset
itself.set.delete(value)
- Deletes a value. If thevalue
exists when this method is called, it returnstrue
; otherwise, it returnsfalse
.set.has(value)
- Returnstrue
if thevalue
exists in the set, otherwise returnsfalse
.set.clear()
- Clears the set.set.size
- Returns the number of elements.
The main feature of Set
is that using the same value to call set.add(value)
multiple times will not cause any changes. This is why each value in the Set
can only occur once.
Counting (Statistics)#
Let's take a look at the following code. Can you understand what it does without comments, and the underlying principle?
const uid = () =>
String(
Date.now().toString(32) +
Math.random().toString(16)
).replace(/\./g, '')
const size = 1000000
const set = new Set(new Array(size)
.fill(0)
.map(() => uid()))
console.log(
size === set.size ? 'all ids are unique' : `not unique records ${size - set.size}`
)
Yes, it is actually checking the uid
function to see if the generated ids are unique. Interestingly, this is how Set
is used to check uniqueness. Why can we judge whether the created uuids are unique by comparing set.size
with size
?
It is because the values in the Set
collection can only occur once. Therefore, when using map
and uid()
to generate values, if there are duplicate values, they will not be added to the set. By utilizing this feature, we can count whether there are duplicate values in the initial example and the number of duplicates:
- By calling
set.size
, if all values are unique, the length of the set after mapping should be the same as the initially definedsize
. - Otherwise, the difference between
size
andset.size
is the number of duplicates.
By using Set
properly, not only can the code be more efficient, but it can also be more concise and easier to understand. Therefore, when encountering scenarios such as deduplication, statistics, and counting, if you previously hesitated and considered using arrays, you can pay more attention to whether Set
can improve code efficiency.