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:

  1. If we modify the HTML code, the element is not calledtrafficThis function will not work.
  2. If we want to reuse this function somewhere else, we have to recreate this in that placetrafficObject.

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

  1. Pass an external variable into a function as an argumenttraffic
  2. Decouple state data from function and abstract datastateList

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

  1. Function should be well encapsulated to reduce function coupling
  2. To ensure that functions do not directly use or modify external variables, they should be passed in as arguments
  3. A function is the smallest unit of processing data. It contains data and processing
  4. Good data abstraction, the data used to form objects or arrays, can improve the reusability of functions
  5. It can improve the reusability and flexibility of functions by abstracting the process and forming independent functions
  6. The asynchronous operation is performedpromiseTo 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]