This article is designed to help you master several important high-level programming techniques in JavaScript, such as: high-level functions, high-level singleton patterns, the idea of lazy functions, the idea of cochemical functions, and the idea of flat functions.

High-level programming

In this article, we’ll focus on what high-level programming ideas are in JavaScript, and what practical uses they have in a project.

The singleton design pattern

Use a single instance to manage the relevant characteristics of the current thing, generally referring to attributes and methods, similar to the realization of the characteristics of grouping, binding all the characteristics of an instance in a group.

Consider a simple singleton design pattern:

let module1 = (function () {
  function tools() {}
  function share() {}

  return {
    name: 'house', tools }; }) (); module1.tools();Copy the code

Here we can expose the methods defined by module1 to external modules.

There is also a closure based singleton pattern called advanced Singleton design pattern. Before vUE/React came out, it was the most commonly used modular idea for team collaboration, and it was often divided by this module.

The closure form of the singleton pattern is as follows:

let A = (function () {
  function fn() {
    B.getXxx();
  }
  function query() {}

  return {
    query
  }
})();

let B = (function () {
  function fn() {}
  function getXxx() {}
  
  A.query();

  return {
    getXxx
  }
})(); 
Copy the code

In the example above, we can define module A and module B, declare functions in our module, and then expose them to each other’s module. This is the earliest idea of modularity, and is also the advanced singleton design pattern.

The inertia function

Let’s start with an example that encapsulates a function that gets an element’s style.

To get a style, we usually think of window.getComputedStyle and Element.CurrentStyle apis, but their execution is also conditional.

For example, the former is only compatible with Google’s browser, while the latter is compatible with Internet Explorer.

function getCss(element, attr) {
  if ('getComputedStyle' in window) {
    return window.getComputedStyle(element)[attr];
  }
  return element.currentStyle[attr];
} 

getCss(document.body, 'margin');
getCss(document.body, 'padding');
getCss(document.body, 'width'); 
Copy the code

From this example, we can see that if you need to get the style of the element three times, it is obvious that each entry function needs to determine whether the method is compatible, which is unnecessary waste. Best solution – lazy function idea.

In layman’s terms, lazy functions are initialized the first time they are rendered, and if they are executed the second time, we need to use closure thinking to solve this problem.

function getCss(element, attr) {
  if ('getComputedStyle' in window) {
    getCss = function (element, attr) {
      return window.getComputedStyle(element)[attr];
    };
  } else {
    getCss = function (element, attr) {
      return element.currentStyle[attr];
    };
  }
  // In order to get value the first time
  return getCss(element, attr);
}
getCss(document.body, 'margin');
getCss(document.body, 'padding');
getCss(document.body, 'width'); 
Copy the code

GetComputedStyle (); getComputedStyle (); getComputedStyle (); getComputedStyle (); getCss (); In this way, every time the function is executed, there will be less a level of judgment, to a certain extent, improve the js running speed.

So let’s see, where is this inertia?

GetCss (getCss) ¶ getCss (getCss) (getCss) (getCss) (getCss) (getCss) (getCss) (getCss) (getCss) (getCss)); On each subsequent execution, the small function getCss is executed.

Kerphysicochemical function

A keriochemical function means passing parameters to a function in steps, passing some of the parameters at a time, and returning a more specific function that receives the rest of the parameters. This can be nested with multiple functions that receive some of the parameters until the final result is returned.

The most basic currie split

/ / function
function add(a, b, c) {
  return a + b + c;
}

// Corylized function
function addCurrying(a) {
  return function (b) {
    return function (c) {
      returna + b + c; }}}// Call the original function
add(1.2.3); / / 6

// Call the curryized function
addCurrying(1) (2) (3) / / 6
Copy the code

The function that runs, addCurrying, returns a single function and uses the next argument until all three arguments have been passed, and the last function that returns runs the sum internally, taking full advantage of closures.

Packaging and chemical general

The above crimization functions do not involve higher-order functions, nor are they generic enough to convert functions with arbitrary or unknown parameters.

Let’s encapsulate a generic Curryization conversion function that converts any function to curryization.

// The add parameter is not fixed
function add (a,b,c,d) {
  return a+b+c+d
}

function currying (fn, ... args) {
  // Fn. Length Specifies the sum of the parameters of the callback function
  // The sum of arguments following the args.length currying function
  N, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
  if (fn.length === args.length) {  
    returnfn(... args) }else {
    // Continue to pass the parameter in step by step
    return function anonymous(. newArgs) {
      // Combine the arguments passed first with the arguments passed later
      let allArgs = [...args, ...newArgs]
      returncurrying(fn, ... allArgs) } } }let fn1 = currying(add, 1.2) / / 3
let fn2 = fn1(3)  / / 6
let fn3 = fn2(4)  / / 10
Copy the code

The idea of kosher functions

Use closures to store information in advance (the idea of preprocessing)

Let’s use a sum problem to describe it:

function currying(. outerArgs) {
  return function anonymous (. innerArgs) {
    let args = outerArgs.concat(innerArgs)
    return args.reduce((previousValue, currentValue) = > previousValue + currentValue)
  }
}

let fn1 = currying(1.2)
let fn2 = fn1(3)
let fn3 = fn2(4)
Copy the code

Add to the idea of what pretreatment is:

When fn1 is executed for the first time, a function execution context is created, and 1 and 2 are stored in the local variables in the context. When fn2 and fn3 are executed, the return result of fn1 needs to be obtained first, using the stored 1 and 2 parameters. This mechanism forms the closure

If you don’t understand Reduce, take a look:

Case 1: Reduce does not have the second parameter

let Array = [10.20.30.40.50];
Array.reduce(previousValue, currentValue, currentIndex, arr){
  return previousValue + currentValue;
}
Copy the code
  • When the function is first fired, previousValue is the first item (10), currentValue is the second item (20)
  • The second time the function is fired, the previousValue isThe return value of the callback function(30), currentValue is the third item (30)
  • When the function is fired for the X time, the previousValue isThe return value of the callback functionCurrentValue firstX+1item
  • CurrentIndex: corresponds tocurrentValueThe index of the value
  • Arr: is a constant array itself

Case 2: Reduce has a second parameter

let Array = [10.20.30.40.50];
Array.reduce((previousValue, currentValue, currentIndex, arr){
  return previousValue + currentValue;
}, 0)
Copy the code
  • When the function is first fired, previousValue is the second argument (0), currentValue is the first item (10)
  • The second time the function is fired, the previousValue isThe return value of the callback function(10), currentValue is the second item (20)
  • When the function is fired for the X time, the previousValue isThe return value of the callback functionCurrentValue firstXitem
  • CurrentIndex: corresponds tocurrentValueThe index of the value
  • Arr: is a constant array itself

Compose function

Compose composition function: Flattens calls to multiple layers of functions

Compose function is often used to solve the flattening problem of function calls based on the idea of reduce physicochemical functions:

In projects, we often encounter a problem like this:

const fn1 = x= > x + 10;
const fn2 = x= > x - 10;
const fn3 = x= > x * 10;
const fn4 = x= > x / 10;

console.log(fn3(fn1(fn2(fn1(4)))))
Copy the code

In the example above, it is clear that the hierarchy is deeply nested, and this is where the idea of calling a function flat is needed

Function call flattening: If it is a multilevel nested function, pass one function call to the next function as an argument to another function

Solution: Execute from left to right

/ * * *@param  Funcs stores functions (arrays) that are executed in order => [fn1, fn3, fn2, fn4] *@param  Args stores the argument information (array) to be passed for the first function execution => [5] */
function compose(. funcs) {
  return function anonymous(. args) {
    if(funcs.length === 0) return args;
    if(funcs.length === 1) return funcs[0] (... args);// Funcs have multiple functions
    return funcs.reduce((n, func) = > {
      // First execution:
      // n: argument executed by the first function func: the first function
      // Second execution:
      // the value of n: the value returned after the last func execution, passed as an argument to the next function to execute func: the second function
      return Array.isArray(n) ? func(... n) : func(n); }, args) } }let res = compose(fn1, fn3, fn2, fn4)(5)

// The process of execution:
console.log(compose()(5)); / / = > 5
console.log(compose(fn1)(5)); / / = > 5 + 10 = 15
console.log(compose(fn1, fn3)(5)); //=>fn1(5)=15 fn3(15)=150
console.log(compose(fn1, fn3, fn2)(5)); //=>fn1(5)=15 fn3(15)=150 fn2(150)=140
console.log(compose(fn1, fn3, fn2, fn4)(5)); //=>fn1(5)=15 fn3(15)=150 fn2(150)=140 fn4(140)=14
Copy the code

Compose flattening:

  1. Compose ()(5) : for example, if no function argument is passed in for compose(), return the following argument, 5

  2. Compose (fn1, fn3, fn2 fn4) (5), the first execution fn1 (5), then fn1 (5) the results to the fn3, such as fn3 (fn1 (5)) = > fn3 (15), and so on fn2 (fn3 (fn1 (5))) = > fn4 (150). Each subsequent function call takes the result returned by the previous function, which leads to the array.prototype.reduce method

  3. The second parameter of reduce is set to ensure that n is the returned result of each function (n is 5 for the first time, and func is fn1), which just needs to be executed by fn1(5) function. Therefore, the necessity of the second parameter is to ensure that the returned result of each callback function is number

Functional programming versus imperative programming

Callback function: pass a function as a value to another function and execute it in another function (this is important for functional programming)

Functional programming: focus on results, don’t care about the process, give the process to others, reflect the idea of function encapsulation (advocate)

Imperative programming: focus on the process and need to implement the process yourself

Functional programming: how to encapsulate the logic into API methods so that we can retrieve the desired results as long as we call the API methods

let arr = [10.20.30.40.50];

let res = arr.reduce((n, item) = > {
  return n + item;
});
Copy the code

Imperative programming: Implementing the main logic in code and focusing on process

let res = 0;
for (let i = 0; i < arr.length; i++) {
  res += arr[i];
}
Copy the code

Read three things ❤

If you found this post helpful, I’d like to invite you to do three small favors for me:

  1. Like, forward so that more people can see the introduction content (collection does not like, are playing rogue!!)
  2. Follow the public account “front time house” and share original knowledge irregularly.
  3. In the meantime, look forward to the following article ing

You can also visit my personal blog:

Front end time house: www.javascriptlab.top/