How to write good JavaScript is every front-end engineer has been thinking about the problem, Moon shadow teacher told us some good JavaScript principles, but also taught us some of the skills of how to write good JavaScript, today to continue to learn JavaScript with moon shadow teacher ~~
start
Today we are going to talk about function encapsulation, we are going to write JavaScript code well, that must consider the code encapsulation, we are going to learn through a case study to learn the idea of function encapsulation.
Let’s look at a case of state-switched traffic lights
Case: Asynchronous state switching (traffic lights)
The specific requirement of this example is to simulate the traffic light signal, display different colors at intervals, and switch the state cycle
Unwrapped: Rookie version
This is for us novices, novices, it might read something like this:
const traffic = document.getElementById('traffic');
(function reset(){
traffic.className = 's1';
setTimeout(function(){
traffic.className = 's2';
setTimeout(function(){
traffic.className = 's3';
setTimeout(function(){
traffic.className = 's4';
setTimeout(function(){
traffic.className = 's5';
setTimeout(reset, 1000)},1000)},1000)},1000)},1000); }) ();Copy the code
The rookie version of the code above does what we need, but it has a major design flaw
The first flaw is that the reset function accesses the external environment traffic, which has no meaning inside the function. There are two problems with this:
- If we modify the HTML code, the element is not called
traffic
This function will not work. - If we want to reuse this function somewhere else, we have to recreate this in that place
traffic
Object.
The second flaw is callback hell, where we have to write so many callbacks by hand that it can be cumbersome to add or subtract states
The reason for these problems is that we did not wrap the function!!
Therefore, we need to encapsulate the function. The object of traffic cannot be directly written in the function, nor can the specific data of state switch be directly written in the function
Encapsulating Data: Data Abstraction Edition
Let’s start by abstracting the data, or decoupling it from the function
First, we pass the traffic variable into our start function as an argument to the function, so that there are no variables inside the function body that come entirely from the external environment
We then abstracted the state (data) into an array of objects with the state name and wait time last. We pass the array into our start function and recursively call applyState to do the state switch.
In this way, the function is encapsulated and the data is abstracted out. When we modify the data, we do not need to modify the content in the function body, which greatly improves the encapsulation of our function.
const traffic = document.getElementById('traffic');
const stateList = [
{state: 'wait'.last: 1000},
{state: 'stop'.last: 3000},
{state: 'pass'.last: 3000},];function start(traffic, stateList){
function applyState(stateIdx) {
const {state, last} = stateList[stateIdx];
traffic.className = state;
setTimeout(() = > {
applyState((stateIdx + 1) % stateList.length);
}, last)
}
applyState(0);
}
start(traffic, stateList);
Copy the code
Data abstraction is the definition and aggregation of data into objects that can be processed by a particular process. In a nutshell, it is the structuring of data.
Two improvements have been made
- Pass an external variable into a function as an argument
traffic
- Decouple state data from function and abstract data
stateList
Both improve function encapsulation and reusability
Encapsulating behavior: Process Abstract edition
The three principles that we talked about earlierProcedural abstractionIn, as we know, not only candataYou can abstract it, you can abstract itprocessAbstract, let’s use the abstraction process to encapsulate functions.
This time we abstract out the two operations of the process: ① change the class name and ② wait time
Change the name of the class to setState
function setState(state){
traffic.className = state;
}
Copy the code
② Wait time is to use wait function to encapsulate setTimeout. We abstract the operation of timer and make promise to improve the readability of our code
function wait(ms) {
return new Promise(resolve= > setTimeout(resolve, ms));
}
Copy the code
Encapsulate the setTimeout function as a wait function that returns a Promise. Then, with async/await syntax, you can write asynchronous code in a synchronous code style
Is this code much more readable than the previous code? And we’ve abstracted it all out
const traffic = document.getElementById('traffic');
function wait(time){
return new Promise(resolve= > setTimeout(resolve, time));
}
function setState(state){
traffic.className = state;
}
async function start(){
//noprotect
while(1){
setState('wait');
await wait(1000);
setState('stop');
await wait(1000);
setState('pass');
await wait(1000);
}
}
start();
Copy the code
Encapsulation behavior: Macro version (Encapsulation polling operation)
Here we encapsulate in setState the operations that change the class name and wait time,
Encapsulate the setup states as functions and take these states as parameters to the polling function
async function setState(state, ms){
traffic.className = state;
await wait(ms);
}
Copy the code
The main thing is to encapsulate the polling function
Abstract the loop playback as a polling function for switching states
function poll(. fnList){
let stateIndex = 0;
return async function(. args){
let fn = fnList[stateIndex++ % fnList.length];
return await fn.apply(this, args); }}Copy the code
You can use it like this
let trafficStatePoll = poll(setState.bind(null.'wait'.1000),
setState.bind(null.'stop'.3000),
setState.bind(null.'pass'.3000));
Copy the code
Finally, the complete code looks like this
const traffic = document.getElementById('traffic');
function wait(ms) {
return new Promise(resolve= > setTimeout(resolve, ms));
}
function poll(. fnList){
let stateIndex = 0;
return async function(. args){
let fn = fnList[stateIndex++ % fnList.length];
return await fn.apply(this, args); }}async function setState(state, ms){
traffic.className = state;
await wait(ms);
}
let trafficStatePoll = poll(setState.bind(null.'wait'.1000),
setState.bind(null.'stop'.1000),
setState.bind(null.'pass'.1000));
(async function() {
// noprotect
while(1) {
await trafficStatePoll();
}
}());
Copy the code
This increases the flexibility of our function. The details of our state switch can be changed, and by abstracting the states, the number of changed states can be easily modified by adding them directly to the trafficStatePoll
conclusion
- Function should be well encapsulated to reduce function coupling
- To ensure that functions do not directly use or modify external variables, they should be passed in as arguments
- A function is the smallest unit of processing data. It contains data and processing
- Good data abstraction, the data used to form objects or arrays, can improve the reusability of functions
- It can improve the reusability and flexibility of functions by abstracting the process and forming independent functions
- The asynchronous operation is performed
promise
To improve the readability of functions
More related posts
[Youth Training camp] Teacher Yue Ying told me the three principles of writing good JavaScript — each is responsible for his own work
[Youth Training Camp] Teacher Yue Ying told me the three principles of good JavaScript writing — component encapsulation
[Youth Training Camp] Teacher Yue Ying told me the three principles of good JavaScript – process abstraction
[Youth Training Camp] Teacher Yue Ying told me four skills to write good JavaScript — style first
[Youth Training Camp] Teacher Yue Ying told me four skills to write good JavaScript — make sure it is correct
You can also pay attention to the column: [Youth Training Camp notes]