расширенные функции javascript, о которых вы могли не знать

внешний интерфейс JavaScript
расширенные функции javascript, о которых вы могли не знать

предисловие

Функции высшего порядка — это функции, которые работают с другими функциями, либо принимая их в качестве аргументов, либо возвращая их. Проще говоря, функция высшего порядка — это функция, которая получает функцию в качестве аргумента или возвращает функцию в качестве вывода.

НапримерArray.prototype.map,Array.prototype.filter,Array.prototype.reduceВсе функции высшего порядка.

Добро пожаловать в другие статьи серии js

Хвостовые вызовы и хвостовая рекурсия

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

function g(x) {
  console.log(x)
}
function f(x) {
  return g(x)
}
console.log(f(1))
//上面代码中,函数f的最后一步是调用函数g,这就是尾调用。

В приведенном выше коде последним шагом функции f является вызов функции g, которая является хвостовым вызовом. Хвостовой вызов не обязательно появляется в конце функции, если это последний шаг.

Функция вызывает сама себя, что называется рекурсией. Если хвост вызывает сам себя, это называется хвостовой рекурсией. Рекурсия очень требовательна к памяти, поскольку требует одновременного сохранения сотен кадров вызовов и подвержена ошибкам переполнения стека. Но для хвостовой рекурсии, поскольку есть только один кадр вызова, ошибка переполнения стека никогда не произойдет.

function factorial(n) {
  if (n === 1) {
    return 1
  }
  return n * factorial(n - 1)
}

Приведенный выше код представляет собой факториальную функцию. Чтобы вычислить факториал n, необходимо сохранить не более n данных вызова, а сложность равна O (n). Если он переписан как хвостовой вызов, сохраняется только одна запись вызова, и сложность O (1).

function factor(n, total) {
  if (n === 1) {
    return total
  }
  return factor(n - 1, n * total)
}

Последовательность Фибоначчи также может использоваться для хвостовых вызовов.

function Fibonacci(n) {
  if (n <= 1) {
    return 1
  }
  return Fibonacci(n - 1) + Fibonacci(n - 2)
}
//尾递归
function Fibona(n, ac1 = 1, ac2 = 1) {
  if (n <= 1) {
    return ac2
  }
  return Fibona(n - 1, ac2, ac1 + ac2)
}

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

В математике и информатике каррирование — это метод преобразования функции, которая принимает несколько аргументов, в ряд функций, которые принимают один аргумент.Каррирование — это процесс преобразования функции с большим количеством параметров в функцию с меньшим количеством параметров.
Например

//普通函数
function fn(a, b, c, d, e) {
  console.log(a, b, c, d, e)
}
//生成的柯里化函数
let _fn = curry(fn)

_fn(1, 2, 3, 4, 5) // print: 1,2,3,4,5
_fn(1)(2)(3, 4, 5) // print: 1,2,3,4,5
_fn(1, 2)(3, 4)(5) // print: 1,2,3,4,5
_fn(1)(2)(3)(4)(5) // print: 1,2,3,4,5

Реализация функции лечения

// 对求和函数做curry化
let f1 = curry(add, 1, 2, 3)
console.log('复杂版', f1()) // 6

// 对求和函数做curry化
let f2 = curry(add, 1, 2)
console.log('复杂版', f2(3)) // 6

// 对求和函数做curry化
let f3 = curry(add)
console.log('复杂版', f3(1, 2, 3)) // 6

// 复杂版curry函数可以多次调用,如下:
console.log('复杂版', f3(1)(2)(3)) // 6
console.log('复杂版', f3(1, 2)(3)) // 6
console.log('复杂版', f3(1)(2, 3)) // 6

// 复杂版(每次可传入不定数量的参数,当所传参数总数不少于函数的形参总数时,才会执行)
function curry(fn) {
  // 闭包
  // 缓存除函数fn之外的所有参数
  let args = Array.prototype.slice.call(arguments, 1)
  return function() {
    // 连接已缓存的老的参数和新传入的参数(即把每次传入的参数全部先保存下来,但是并不执行)
    let newArgs = args.concat(Array.from(arguments))
    if (newArgs.length < fn.length) {
      // 累积的参数总数少于fn形参总数
      // 递归传入fn和已累积的参数
      return curry.call(this, fn, ...newArgs)
    } else {
      // 调用
      return fn.apply(this, newArgs)
    }
  }
}

Использование карри

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

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

function checkByRegExp(regExp, string) {
  return regExp.text(string)
}

checkByRegExp(/^1\d{10}$/, '18642838455') // 校验电话号码
checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@163.com') // 校验邮箱

Каждый раз, когда мы выполняем проверку, нам нужно вводить строку регулярного выражения, а при проверке одного и того же типа данных нам нужно писать одно и то же регулярное выражение несколько раз, что приводит к низкой эффективности при его использовании, а также потому, что сама функция checkByRegExp Функция полезности не имеет никакого смысла. На этом этапе мы можем использовать каррирование для инкапсуляции функции checkByRegExp, чтобы упростить написание кода и улучшить его читаемость.

//进行柯里化
let _check = curry(checkByRegExp)
//生成工具函数,验证电话号码
let checkCellPhone = _check(/^1\d{10}$/)
//生成工具函数,验证邮箱
let checkEmail = _check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/)

checkCellPhone('18642838455') // 校验电话号码
checkCellPhone('13109840560') // 校验电话号码
checkCellPhone('13204061212') // 校验电话号码

checkEmail('test@163.com') // 校验邮箱
checkEmail('test@qq.com') // 校验邮箱
checkEmail('test@gmail.com') // 校验邮箱

Длина параметра каррированной функции

В реализации каррирования функции fn.length используется для представления количества параметров функции.Представляет ли fn.length количество всех параметров функции? Не совсем.

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

((a, b, c) => {}).length
// 3

((a, b, c = 3) => {}).length
// 2

((a, b = 2, c) => {}).length
// 1

((a = 1, b, c) => {}).length
// 0

((...args) => {}).length
// 0

const fn = (...args) => {
  console.log(args.length)
}
fn(1, 2, 3)
// 3

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

Compose — это комбинированная функция, которая последовательно выполняет подфункции.Вывод одной функции является входным параметром другой функции.Как только первая функция начнет выполняться, она выведет и выполнит последующие функции, такие как домино.

const greeting = name => `Hello ${name}`
const toUpper = str => str.toUpperCase()

toUpper(greeting('Onion')) // HELLO ONION

Возможности функции составления

  • compose принимает функцию в качестве параметра, выполняется справа налево и возвращает функцию типа
  • Все параметры fn() передаются в самую правую функцию, а после получения результата — в предпоследнюю, а затем по очереди

Реализация компоновки

var compose = function(...args) {
  var len = args.length // args函数的个数
  var count = len - 1
  var result
  return function func(...args1) {
    // func函数的args1参数枚举
    result = args[count].call(this, args1)
    if (count > 0) {
      count--
      return func.call(null, result) // result 上一个函数的返回结果
    } else {
      //回复count初始状态
      count = len - 1
      return result
    }
  }
}

Например

var greeting = (name) =>  `Hello ${name}`
var toUpper = str => str.toUpperCase()
var fn = compose(toUpper, greeting)
console.log(fn('jack'))

Знакомый порядок выполнения загрузчиков в webpack — справа налево, потому что webpack выбирает метод компоновки и последовательно выполняет загрузчики справа налево, а каждый загрузчик — это функция.

rules: [
  { test: /\.css$/, use: ['style-loader', 'css-loader'] }
]

Как и выше, веб-пакет использует загрузчик стилей и загрузчик css.Он сначала загружает файлы .css с помощью загрузчика css, а затем загрузчик стилей внедряет внутренние стили в наши html-страницы.

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

const compose = (...fns) => {
  return fns.reduce(
    (prevFn, nextFn) => {
      return value =>prevFn(nextFn(value)) 
    },
    value => value
  )
}

рекомендуемая статья