Эта статья поможет вам освоить продвинутые навыки программирования JS! яростный!

JavaScript
Эта статья поможет вам освоить продвинутые навыки программирования JS! яростный!

Примечание редактора:Эта статья призвана помочь вам освоить несколько важных навыков программирования на JavaScript, например:高阶函数,高级单例模式,惰性函数思想,柯理化函数思想,compose扁平化函数思想.

расширенное программирование

В этой статье мы в основном объясним, каковы идеи высокоуровневого программирования в JavaScript и каково их практическое применение в проекте?

одноэлементный шаблон проектирования

Используйте отдельный экземпляр для управления соответствующими характеристиками текущей вещи, обычно ссылаясь на属性а также方法, аналогично реализации сгруппированных функций, связывает все описания функций экземпляра в группу.

Давайте взглянем на простой шаблон проектирования singleton:

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

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

Здесь мы можем предоставить методы, определенные модулем module1, для использования внешними модулями.

Существует также одноэлементный шаблон, основанный на реализации замыкания, который называется расширенный одноэлементный шаблон проектирования.До появления vue/react это была наиболее часто используемая идея модульности для совместной работы в команде.模块划分.

Паттерн singleton в виде замыкания выглядит следующим образом:

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

  return {
    query
  }
})();

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

  return {
    getXxx
  }
})(); 

В приведенном выше примере мы можем определитьA、BДва модуля сначала объявляют функции в своих собственных модулях, а затем предоставляют их для использования модулями друг друга.Это самая ранняя идея модуляризации.高级单例设计模式.

ленивая функция

Давайте сначала рассмотрим такой пример, инкапсулирующий функцию для получения стиля элемента.

чтобы получить стили, мы обычно думаем оwindow.getComputedStyleа такжеelement.currentStyleЭто два API, но их выполнение тоже условно.

Например: только первыйGoogleсовместим с браузером, последнийIEСовместимость с браузером.

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

Из этого примера видно, что если нам нужно получить стиль элемента три раза, очевидно, каждый раз, когда мы входим в функцию, нам нужно судить, совместим ли метод или нет, что приводит к ненужным потерям. Лучшее решение -惰性函数Мысль.

С точки зрения непрофессионала,惰性函数Он выполняется при инициализации первого рендеринга.Если второе выполнение все еще имеет тот же эффект, нам нужно использовать идею замыкания для решения этой проблемы.

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

В этой функции при первом выполнении функции getCss мы уже можем определитьgetComputedStyleСовместим или нет, так что второй раз не надо再判断Теперь по результату, возвращаемому после первого суждения, напрямую определяем, какую функцию использовать для выполнения второй и третьей функции.API, так что при каждом выполнении функции будет меньше一层判断, в определенной степени улучшить скорость работы js.

Итак, давайте посмотрим, это惰性Где это проявляется?

Процесс: Собственно, впервыеgetCssФункция выполняется и создается全局作用域вниз私有执行上下文, и в нем возродилсяgetCssфункции, переназначьте ее ссылочный адрес глобальной функцииgetCss, Привести к全局внизgetCssне может быть освобожден, образуя闭包. Каждый раз, когда он выполняется в будущем, выполняется небольшая функция внутриgetCss.

Функция отверждения

Значение функции отвержденияЭто шаг за шагом передавать параметры в функцию, каждый раз передавать часть параметров и возвращать более конкретную функцию для получения оставшихся параметров, в которой несколько слоев таких функций, которые получают частичные параметры, могут быть вложены до окончательного результата. возвращается.

Самый простой карри-сплит

// 原函数
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

Каррированная функцияaddCurryingкаждый раз返回值都为一个函数и использовать下一个参数作为形参, пока не будут переданы три параметра, последняя возвращенная функция выполняется внутри求和操作, фактически полностью используется闭包的特性быть реализованным.

Пакет отвержденный общего назначения

Вышеупомянутая функция каррирования не включает高阶函数, не универсален,Невозможно преобразовать функцию с произвольным или неизвестным количеством параметров.

Давайте инкапсулируем общийКаррирование функций преобразования, который может преобразовать любую функцию в каррирование.

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

Идея функции отверждения

использовать闭包保存机制, хранить некоторую информацию заранее (идея препроцессинга)

мы используем求和的题описать:

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)

Дополнение В чем идея препроцессинга:

во-первыхfn1При выполнении функции создается контекст выполнения функции, 1, 2 сохраняются в локальных переменных контекста и выполняются позжеfn2 fn3, необходимо получитьfn1Возвращаемый результат, используя сохраненные параметры 1, 2, формируется этот механизм闭包

не понимаюreduceдрузья смотрите здесь:

Случай 1: когда у сокращения нет второго параметра

let Array = [10, 20, 30, 40, 50];
Array.reduce(previousValue, currentValue, currentIndex, arr){
  return previousValue + currentValue;
}
  • Когда функция запускается в первый раз, предыдущее значение является первым элементом (10), currentValue — второй элемент (20)
  • Когда функция запускается во второй раз, предыдущее значение равно回调函数的返回值(30), currentValue — третий элемент (30)
  • Когда функция запускается в X-й раз, предыдущее значение равно回调函数的返回值, currentValue является первымX+1пункт
  • currentIndex: соответствуетcurrentValueзначение индекса
  • arr: сам константный массив

Случай 2: когда у сокращения есть второй параметр

let Array = [10, 20, 30, 40, 50];
Array.reduce((previousValue, currentValue, currentIndex, arr){
  return previousValue + currentValue;
}, 0)
  • Когда функция запускается в первый раз, предыдущее значение является вторым параметром (0), currentValue — это первый элемент (10)
  • Когда функция запускается во второй раз, предыдущее значение равно回调函数的返回值(10), currentValue — второй элемент (20)
  • Когда функция запускается в X-й раз, предыдущее значение равно回调函数的返回值, currentValue является первымXпункт
  • currentIndex: соответствуетcurrentValueзначение индекса
  • arr: сам константный массив

составить функцию

функция составления композиции: выравнивание многоуровневых вложенных вызовов функций.

Функция компоновки часто основана наreduce 柯理化函数Думал решить函数调用扁平化Эта проблема:

В проекте мы часто сталкиваемся с такой проблемой:

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

В приведенном примере очевидно, что иерархия глубоко вложена, и в это время необходимо вызвать函数扁平化подумал о

Выравнивание вызовов функций: Если это многоуровневая вложенная вызывающая функция, вызовите одну функцию и передайте ее следующей функции в качестве фактического параметра другой функции.

Решение: выполнить последовательно слева направо

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

Процесс выполнения выравнивания функции compose:

  1. compose()(5), если в функцию compose() не переданы параметры функции, следующие параметры будут возвращены напрямую 5

  2. compose(fn1, fn3, fn2, fn4)(5), сначала выполнить fn1(5), а затем передать результат fn1(5) в fn3, например fn3(fn1(5)) => fn3(15), с И так далее fn2(fn3(fn1(5))) => fn4(150), тогда每一次函数调用的参数都是前一个函数返回的结果, мы, естественно, подумали оArray.prototype.reduceметод

  3. настраиватьreduceВторой параметр – унификацияnдля каждого возвращаемого результата функции (первый разn5,funcfn1), просто нужно позволитьfn1(5)Выполнение функции, видно, что необходимость второго параметра заключается в унификации возвращаемого результата каждой функции обратного вызова.number

Функциональное программирование против императивного программирования

Перезвоните: передать функцию как значение другой функции и выполнить эту функцию в другой функции (это важное знание для реализации функционального программирования)

функциональное программирование:注重结果, не заботятся о процессе, процесс передается другим, отражая函数封装性思想(продвигать)

императивное программирование:注重过程, вам нужно реализовать процесс самостоятельно

функциональное программирование:Пучок逻辑如何实现Инкапсуляция становится методом API, нам нужно только вызвать метод API в будущем, чтобы получить желаемый результат.

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

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

императивное программирование: реализовать основную логику с помощью кода, сосредоточившись на процессе

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

Прочитав три вещи ❤

Если вы считаете, что этот контент очень полезен для вас, я хотел бы пригласить вас сделать мне три небольших одолжения:

  1. Поставьте лайк и перешлите его, чтобы больше людей могли увидеть вводный контент (если вам не нравится коллекция, это все хулиганство!!)
  2. Обратите внимание на паблик «Front-end Time House» и время от времени делитесь оригинальными знаниями.
  3. В то же время вы можете рассчитывать на последующие статьи, посвященные

Вы также можете зайти в мой личный блог:

Фронт-энд тайм-хаус:www.javascriptlab.top/