No matter how you organize or design your logic, it comes down to a running list, which expresses “When you do what you do.”

Good code should be simple and linear:

dining(); / / to eat
sleeping(); / / sleep
coding(); / / type code
Copy the code

However, the actual business logic can not be so simple, which requires the use of different organizational methods to further encapsulate the original logic ~

The following organizations are mainly introduced:

  • The callback function
  • Chain calls
  • async/await
  • Higher-order functions
  • Chain of Responsibility model

The callback function

function fn(option) {
  option.onCallback("I am the thing that returns.");
}
fn({
  onCallback: function (result) {
    console.log("Display returned data:"+ result); }});Copy the code

Cautionary tale

Use the shockwave code to demonstrate how not to do this:

getData((data) = > {
  getMoreData(data, () = > {
    getPerson((person) = > {
      getPlanet(person, (planet) = > {
        getGalaxy(planet, (galaxy) = > {
          getLoca(galaxy, (local) = > {
            console.log(local);
          });
        });
      });
    });
  });
});
Copy the code

Applicable scenario

In fact, linear and interdependent processes are not really suitable for organizing in this way, but are more suitable for scenarios like bound listening, where callbacks exist as listening events.

That is, the external method is called only once, but the callback is more than one or fired more than once, and whether the callback executes during the process does not affect the subsequent process:

/ * * * * call "to eat" method is not subject to "eat" in subsequent process in the process of "to order" and "drink" and so on behavior influence * * /
dining({
  onOrdering: function (order) {
    console.log("O 'clock" + order);
  },
  onDrinking: function (drink) {
    console.log("Drink"+ drink); }}); sleeping();/ / sleep
coding(); / / type code
Copy the code

Chain calls

There are many built-in methods in JS primitives:

[1.2.3.4]
  .concat(5)
  .filter((item) = > item < 3)
  .map((item) = > item + "test")
  .join(","); / / output 1 test, 2 "test"
Copy the code

Realize the principle of

The former method returns a specific instance (typically this), enabling it to continue calling the method on the instance prototype:

Array.prototype.myconcat = function (inject) {
  return [...this, inject]; // Returns the same Array
};
[1.2.3].myconcat("a").myconcat("b"); / / [1, 2, 3, "a", "b"]
Copy the code

Applicable scenario

There are several characteristics of organizing in this way:

  • Repeated operations on a subject
  • Methods are designed to be relatively independent of each other, divided into valuers and valuers
  • Externally, this part of the logic is highly cohesive

A more common usage scenario is operations on data:

class MyDataBase {
  private hashmap = {};
  public add(key, value) {
    this.hashmap[key] = value;
    return this;
  }
  public remove(key) {
    delete this.hashmap[key];
    return this;
  }
  public export() {
    return this.hashmap; }}console.log(
  new MyDataBase()
    .add("username"."Cannon Zhang")
    .add("password"."123")
    .remove("username")
    .export()
);
Copy the code

Q&A

Improper use of map, filter, forEach, reduce methods in arrays:

function fn1(arr1, arr2) {
  arr1.map((outter) = > {
    arr2.map((inner) = > {
      // Use map to modify the original data
      if (inner.id === outter.id) {
        inner.name = "aaa";
      } else {
        inner.name = "bbb"; }}); });return arr2;
}
// function fn1(arr1, arr2) {
// return arr2.map((item) => ({
/ /... item,
// name:
// typeof arr1.find((match) => match.id === item.id) ! == "undefined"
/ /? "aaa"
// : "bbb",
/ /}));
// }
function fn2(arr) {
  let result = [];
  arr.map((item) = > {
    result.push(item + 1);
  });
  return result;
}
// function fn2(arr) {
// return arr.map((item) => item + 1);
// }
Copy the code

Encapsulating a method requires at least the following:

  • If the input parameter is of reference type, determine whether to modify the original data
  • Distinguish between the use of an assignment and a valuer

Javascript API

async/await

Can be organized in the most concise way, directly describe the task flow:

await dining(); / / to eat
offLights(); / / to turn off the lights
await sleeping(); / / sleep
onLights(); / / turn on the light
await coding(); / / type code
Copy the code

So it’s kind of obvious that the outside doesn’t care what the inside is going through, you just put in, and then you get back the result, right

Using the example

We can change some methods that are callback to async/await:

/ / delay
function delay(time) {
  return new Promise((resolve) = > {
    window.setTimeout(() = > {
      resolve();
    }, time);
  });
}

await delay(1000); / / wait for 1 s
console.log("aaa");
await delay(2000); / / wait for 2 s
console.log("bbb");
Copy the code
  • asyncThe return value of the method isPromiseobject
  • awaitTo be able to returnPromise.resolvePromise.rejectThe value of the
delay(3000).then(function () {
  // Wait for 3s to print 'CCC'
  console.log("ccc");
});
Copy the code

Trigger popover in API mode

if (await Modal.confirm("Are you sure you want to delete?")) {
  // TODO
  console.log("Deleted successfully");
}
Copy the code

How to implement a popover component

Higher-order functions

Function as argument or function as return value:

/** * exchange rate calculator (factory) */
const factory = function (rate) {
  return function (num) {
    return num * rate;
  };
};

const rmb2dollar = factory(0.16); // RMB to USD
const rmd2pound = factory(0.12); // RMB/Sterling

rmb2dollar(100); / / 16
rmb2dollar(1000); / / 160
rmd2pound(100); / / 12
Copy the code

Using the example

During development, you often encounter logic that needs to be injected everywhere in the project but decouples from the injection location, such as burying points, exception catching, interceptors, and so on.

This, in turn, involves faceted programming (AOP), which extracts functionality unrelated to the core business logic module and then “dynamically weaves” it into the business module.

function WithSentryHOC(InnerComp) {
  return class extends React.Component {
    myDivRef = React.createRef();
    componentDidMount() {
      this.myDivRef.current.addEventListener("click".this.handleClick);
    }
    componentWillUnmount() {
      this.myDivRef.current.removeEventListener("click".this.handleClick);
    }
    handleClick = () = > {
      console.log('Send buried point: click on itThe ${this.props.name}Component `);
    };
    render() {
      return (
        <div ref={this.myDivRef}>
          <InnerComp {. this.props} / >
        </div>); }}; }Copy the code
function MyNormalComp(props) {
  return <div>Common components</div>;
}

const MyCompWithSentry = WithSentryHOC(MyNormalComp);

function App() {
  return <MyCompWithSentry name="One of my components" />;
}
Copy the code

AOP is more like a complement to OOP, making the overall project logic more “three-dimensional”

Chain of Responsibility model

  1. The request is processed by all the processors and there are no terminations
  2. In a processor chain execution request, if one processor executes and finds that it does not conform to the custom rules, the process is stopped and the remaining unexecuted processors are not executed

The latter is more unique, and the former generally has a better solution, so I’ll talk about the second form later

import Chain from "./chain.js";

// Use Chain to build a Chain, similar to "build a production line"
const peopleChain = new Chain()
  .setNextHandler(function (next, data) {
    if (data.gender === "male") {
      console.log("We don't want boys.");
      return;
    }
    next(data);
  })
  .setNextHandler(function (next, data) {
    if (data.age > 30) {
      console.log("Too old for that.");
      return;
    }
    next(data);
  })
  .setNextHandler(function (next, data) {
    console.log("emmmm... Not bad, not bad.");
  });

/* Load different messages to the responsibility chain */
peopleChain.start({
  gender: "male".age: 21});// Output 'we don't want men'

peopleChain.start({
  gender: "female".age: 48});// output 'too old'

peopleChain.start({
  gender: "female".age: 18});/ / output 'emmmm... Not bad, not bad. '
Copy the code

Some business logic is better suited to organizing code with chains of responsibility:

  • Interview process: one is technical, two is team, and three is supervisor, one of which is interrupted
  • Login process: Deal with login failure results, such as black production, information expiration, etc

Source code and detailed explanation

The advantage of the responsibility chain is that all the handlers in the chain are decoupled. In practice, the responsibility chain can be designed to move forward and backward, and each chain can reference each other

conclusion

These are common problem-solving ideas, and there are many interesting ones, such as:

  • React Context
  • React Context
  • React Component Composition

The purpose of this document is to inspire you to try to find the optimal solution to a problem by illustrating common ways of organizing logic. All the writing methods involved in the following is just to throw a brick to introduce jade, I hope we do not form a qualitative thinking ~

Understanding an objective fact can be divided into categories, but understanding a way of thinking cannot be in this way, because good ways of thinking have common points