This article takes you to master JS advanced programming skills! fierce!

This article takes you to master JS advanced programming skills! fierce!

Editor's recommended language: This article is designed to help you master several important higher-order programming skills in JavaScript, such as: , , , , compose .

High-level programming

In this article, we will mainly explain what are the high-level programming ideas in JavaScript, and what practical uses do they have in projects?

Singleton design pattern

A separate instance for managing the current correlation characteristics of things, refers and , similar to the implementation of packet features, all the features described one example of a packet in the binding.

Take a look at a simple singleton design pattern:

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

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

Here we can expose the methods defined by the module1 module for use by external modules.

There is also a singleton pattern based on closures called: advanced singleton design pattern. Before vue/react came out, it was the most commonly used modular idea for teamwork, and it was often used for this .

The singleton pattern in the form of closure 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
  }
})(); 
 

In the above example, we can define A Btwo modules separately, first declare the function in our own module, and then expose it to the other's module. This is the earliest modular idea, too .

Lazy function

Let's first look at such an example, encapsulating a function to get the element style.

Gets the style, we usually think of window.getComputedStyleand element.currentStylethese two API, but their execution is conditional.

For example: the former is only Googlecompatible with IEbrowsers , while the latter is compatible with browsers.

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'); 
 

From this example, we can see that if you need to get the style of the element three times, obviously you need to judge whether the method is compatible or not every time you enter the function, which causes unnecessary waste. The best solution- thinking.

In layman's terms, the initialization is executed during the first rendering. If the second execution is still the same effect, we need to use the idea of closure 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];
    };
  }
  // 
  return getCss(element, attr);
}
getCss(document.body, 'margin');
getCss(document.body, 'padding');
getCss(document.body, 'width'); 
 

In this function, when we execute the getCss function for the first time, we can already determine getComputedStylewhether it is compatible or not, so there is no need for the second time . According to the returned result of the first judgment, the second time is directly determined , Which one is used for the third function execution? In APIthis way, every time the function is executed, there will be less , which improves the running speed of js to a certain extent.

So let s look at this where is reflected?

Process: In fact, it was the first time the getCssfunction was executed to create the next one , and the getCssfunction was regenerated in it, and its reference address was re-assigned to the global function getCss, causing the next one to getCssbe unable to be released, forming one . Each time it is executed in the future, the small function inside is executedgetCss .

Curriculum function

Curriculum function meaning of is to pass parameters to the function step by step, pass some parameters each time, and return a more specific function to receive the remaining parameters. In the middle, multiple layers of functions that receive some parameters can be nested until the final result is returned. .

The most basic currying split

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

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

// 
add(1, 2, 3); //6

// 
addCurrying(1)(2)(3) //6
 

It is currying function addCurryingeach time and use , until after the three parameters are passed, the function returns inside the last execution , in fact, is full use of to achieve.

Package Ke Lihua general type

The currying function above is not involved , nor is it universal, and cannot be converted to a function with an arbitrary or unknown number of formal parameters. .

Let's encapsulate a universal currying conversion function , which can convert any function into currying.

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

function currying (fn, ...args) {
  //fn.length  
  //args.length currying    
  // add (a,b,c,d)  currying(add,1,2,3,4)
  if (fn.length === args.length) {  
    return fn(...args)
  } else {
    //  newArgs  
    return function anonymous(...newArgs) {
      //   
      let allArgs = [...args, ...newArgs]
      return currying(fn, ...allArgs)
    }
  }
}

let fn1 = currying(add, 1, 2) //3
let fn2 = fn1(3)  //6
let fn3 = fn2(4)  //10
 

Curriculum function thought

use and store some information in advance (the idea of preprocessing)

We use one to describe:

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)
 

Supplement what is the thought of preprocessing:

In the first fn1time the function is executed, it creates a function execution context, the local variables 1, 2 stored in the context of the day, after the execution fn2 fn3time, you need to get fn1a return result, using the stored 1, 2 parameters , This mechanism forms

reduceThe little friend who didn't understand came over:

Case 1: When reduce has no second parameter

let Array = [10, 20, 30, 40, 50];
Array.reduce(previousValue, currentValue, currentIndex, arr){
  return previousValue + currentValue;
}
 
  • When the function is triggered for the first time, previousValue is the first item ( 10), and currentValue is the second item ( 20)
  • When the function is triggered for the second time, previousValue is ( 30), currentValue is the third item ( 30)
  • When the function is triggered for the X time, the previousValue is , and currentValue is the X+1item
  • currentIndex: corresponds to currentValuethe index value
  • arr: is a constant array itself

Case 2: When reduce has a second parameter

let Array = [10, 20, 30, 40, 50];
Array.reduce((previousValue, currentValue, currentIndex, arr){
  return previousValue + currentValue;
}, 0)
 
  • When the function is triggered for the first time, previousValue is the second parameter ( 0), and currentValue is the first item ( 10)
  • When the function is triggered for the second time, previousValue is ( 10), currentValue is the second item ( 20)
  • When the function is triggered for the X time, the previousValue is , and currentValue is the Xitem
  • currentIndex: corresponds to currentValuethe index value
  • arr: is a constant array itself

compose function

compose combination function : flattening multi-level function nested calls

compose function often based on reduce ideas to solve the problem:

In the project, we often encounter such a problem:

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)))))
 

In the above example, it is obvious that the level of nesting is deeper, and the idea of calling is needed at this time

Function call flattening : If it is a multi-level nested call function, after calling a function, pass it to the next function as the actual parameter of another function

Solution: execute sequentially from left to right

/**
 * @param  funcs  ( ) => [fn1, fn3, fn2, fn4]
 * @param  args  ( ) => [5]
 */
function compose(...funcs) {
  return function anonymous(...args) {
    if(funcs.length === 0) return args;
    if(funcs.length === 1) return funcs[0](...args);
    //funcs    
    return funcs.reduce((n, func) => {
      // 
      //n  func 
      // 
      //n func  func 
      return Array.isArray(n) ? func(...n) : func(n);
    }, args)
  }
}

let res = compose(fn1, fn3, fn2, fn4)(5)

// 
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
 

The flat execution process of the compose function:

  1. compose()(5), if no function parameter is passed in the compose() function, it will directly return the following parameter 5

  2. compose(fn1, fn3, fn2, fn4)(5), execute fn1(5) first, and then transfer the result of fn1(5) to fn3, such as fn3(fn1(5)) => fn3(15), By analogy fn2(fn3(fn1(5))) => fn4(150), later , we naturally thought of the Array.prototype.reducemethod

  3. reduceThe second parameter set is to unify nthe return result of each function (the first time nis 5, which funcis fn1), and the fn1(5)function needs to be executed again. It can be seen that the necessity of the second parameter is to unify each time The result of the callback function isnumber

Functional programming and imperative programming

Callback function : pass a function as a value to another function, and execute this function in another function (this is an important knowledge to realize functional programming)

Functional programming :, don t care about the process, leave the process to others to handle, reflect (advocate)

Imperative programming :, you need to implement the process yourself

Functional programming : Encapsulate as an API method, we only need to call the API method in the future to get the desired result.

let arr = [10, 20, 30, 40, 50];

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

Imperative programming : implement the main logic with code and focus on the process

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

After reading three things

If you think this content is quite helpful to you, I would like to invite you to do three small favors for me:

  1. Like, repost so that more people can see the introduction content (collecting or not like, it is a hooligan!!)
  2. Pay attention to the public account "Front-end Time House" and share original knowledge from time to time.
  3. At the same time, you can look forward to follow-up articles ing

You can also come to my personal blog:

Front-end time house: www.javascriptlab.top/