От [if...else...] до [Chain of Responsibility] до [composeAOP], кстати, [передача параметров] решена~

интервью внешний интерфейс JavaScript
От [if...else...] до [Chain of Responsibility] до [composeAOP], кстати, [передача параметров] решена~

🐛 Гнездование гусениц

Я думаю, вы наверняка видели такой код:

if(condition1 === A1){
    if(condition2 === A2){
        ...
    }else if(condition2 === B2){
        ...
    }else if(condition2 === C2){
        ...
    }else{
        ...
    }
}esle if(condition1 === B1){
    ...
    ...
    ...
}else if(condition1 === C1){
    ...
    ...
    ...
}else if(condition1 === D1){
    ...
    ...
    ...
}else{
    ...
}

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

Такое ощущение, что это гусеница. . .

Для того, чтобы выразить это в образе, Бенгуа искренне приглашает художников душиОхранник ЭнтониСделай принципиальную схему, салют! ! ( ̄︶ ̄)↗

Icu1LE.md.jpg

* Источник изображения: Хранитель Энтони, воспроизводится по желанию без разрешения.

Такое письмо всегда будет сопровождаться различными логическими суждениями, неявным вводом-выводом, я действительно не смею его трогать, боюсь, что оно "умрет" на глазах у вас!

🌴Цепочка ответственности Бамбук

Объектив поворачивается к [Цепи ответственности], которая является одним из 23 шаблонов проектирования и относится к поведенческому шаблону, фокусирующемуся на взаимодействии и общении между объектами;

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

Функция каждого элемента подобна бамбуковому соединению, которое можно разбирать независимо и собирать произвольно;

Без лишних слов, код для его реализации выглядит примерно так:

function A1(condition1){
    chainA2.next(chainB2).next(chainC2);
    return condition1 === A1 ? chainA2.setParam(condition2) : 'doNext'
}

function B1(condition1){
    return condition1 === B1 ? ... : 'doNext'
}

function C1(condition1){
   return condition1 === C1 ? ... : 'doNext'
}

function D1(condition1){
   return condition1 === D1 ? ... : 'doNext'
}

...

function A2(condition2){
    return condition2 === A2 ? ... : 'doNext'
}

function B2(condition2){
   return condition2 === B2 ? ... : 'doNext'
}

function C2(condition2){
   return condition2 === C2 ? ... : 'doNext'
}

chainA1.next(chainB1).next(chainC1).next(chainD1)

chainA1.setParam(condition1)

По общему смыслу он похож на бамбук? Каждый раздел (функция ввода, вывода) особенно нагляден. Дело в том, что он развязан и его очень легко собрать~

IcHbcQ.md.jpg

* Источник изображения: Хранитель Энтони, воспроизводится по желанию без разрешения.

основной, код для создания цепочки выглядит следующим образом:

Функция Chain — это расширенная функция, а входной параметр — это функция. Здесь два свойства, next и setParam, добавляются к нему с помощью цепочки прототипов. Входным параметром next также является fn, который используется для установки следующей функции обработки, а setParam используется для передачи исходного входного параметра;

var Chain = function( fn ){
  this.fn = fn;
  this.successor = null;
};
Chain.prototype.next = function( successor ){
  return this.successor = successor;
};
Chain.prototype.setParam = function(){
  var ret = this.fn.apply( this, arguments );
  if ( ret === 'doNext' ){
    return this.successor && this.successor.setParam.apply( this.successor, arguments );
  }
  return ret;
};

🍜Функциональная функция АОП

На самом деле, используя функциональную природу JavaScript, есть гораздо более удобный способ создания цепочки ответственности — АОП.

Простое понимание идеи АОП: Аспектно-ориентированная программа:Идея программирования, которая динамически разрезает код на заданный метод и позицию класса, — это аспектно-ориентированное программирование.

код показывает, как показано ниже:

/**
 * 函数交织(AOP)
 * @param {*} fn
 * @returns
 */

Function.prototype.before = function(fn) {
  const self = this
  return function(...args) {
    const result = fn.apply(null, args)
    return self.call(null, result)
  }
}

Function.prototype.after = function(fn) {
  const self = this
  return function(...args) {
    const result = self.apply(null, args)
    return fn.call(null, result)
  }
}

Пример вызова:

fn1 = step2.before(init).after(step3).after(step4)

//fn1 = init -> step2 -> step3 -> step4

Мы можем произвольно указать и согласовать последовательность выполнения функций;

🥂составить АОП

все еще помню"Благодаря функции компоновки моя дерьмовая гора кода 💩 постепенно становится красивее~"эта статья? На самом деле есть много способов написать композицию! Мы можем реализовать эту версию composeAOP с помощью функций «до» и «после» выше ~

const composeAOP = function(...args) {
  const before = args.pop()
  const start = args.pop()
  if (args.length) {
    return args.reduce(function(f1, f2) {
      return f1.after(f2)
    }, start.before(before))
  }
  return start.before(before)
}

Да тут много людей спрашивают зачем сочинять справа налево исполнять перед ответом? ?

const compose = function(...args) {
  if (args.length) {
    return args.reverse().reduce(function(f1, f2) {
      return f1.after(f2)
    })
  }
}

compose(step4,step3,step2,step1,init)("start")

Позвольте мне рассказать об этом здесь: причина в том, что он имитирует обычный вызов функции слой за слоем, а порядок слоев заворачивается, как чистка лука, снаружи внутрь, справа налево для разбора:

step4(step3(step2(step1(init(...args))))) // 一层层括号像极了洋葱皮

Если вы предпочитаете слева направо или переключитеpop()заshift()или удалите этот слойreverse()можно изменить илиafterзаbefore... дело порядка, хорошо это или плохо,Все дело в предпочтениях~

🎯 Вопросы о передаче параметров! !

Если вы захотите попробовать приведенный выше код на консоли, то нетрудно найти очень серьезную проблему с передачей параметров! ! Эта проблема на самом деле существует в статье «Компоновка оптимизирует Шишан», а также есть тщательные отзывы друзей.

function init(...args){
    console.log(args)
    return [...args,"init"]
}
function step1(...args){
    console.log(args)
    return [...args,"step1"]
}
function step2(...args){
    console.log(args)
    return [...args,"step2"]
}
function step3(...args){
    console.log(args)
    return [...args,"step3"]
}

compose(step3,step2,step1,init)("start")

По мере передачи аргументов размерность массива args продолжает расти.

IcSSVw.md.png

если мы используемflat(Infinity)Сгладьте массив, и передача параметров станет такой:

IcSB8R.md.png

С этим есть большая проблема: нужно сравнивать порядок передачи параметров в массиве! Это очень хлопотно, потому что нет уверенности в том, что параметры процесса будут добавлены, удалены или изменены в один прекрасный день.

Поэтому ожидается, что его можно заменить объектом в качестве параметра,Избавьтесь от оков передачи параметров, чтобы. Например:

{start:"start",init:"init",step1:"step1"......}

Попробуйте напрямую:

function init(...args){
    console.log(JSON.stringify(args))
    return {args:args,init:"init"}
}
function step1(...args){
    console.log(JSON.stringify(args))
    return {args:args,step1:"step1"}
}
function step2(...args){
    console.log(JSON.stringify(args))
    return {args:args,step2:"step2"}
}
function step3(...args){
    console.log(JSON.stringify(args))
    return {args:args,step3:"step3"}
}

compose(step3,step2,step1,init)("start")

получить:

Icr8nW.md.png

Очевидно, это не то, что нам нужно, мы должны продолжать печатать, чтобы найти шаблон:

Ics52e.md.png

вау~

существуетstep3хочу получитьstep1, нужно 2.args[0];

Icse45.md.png

существуетstep2хочу получитьstep1, только 1.args[0];

IcslDC.md.png

В принципе, мы можем придумать:Чтобы получить параметры первых N шагов, достаточно взять N.args[0]

Итак, мы можем попробовать написатьgetCountStepAttr()Функция используется для получения входных параметров первого N-го шага в шаге функции путем вызова свойства объекта!

Давай, расправляй крылья~

function getCountStepAttr(args,N){
    // 需要前第几(N)步的参数
    N = N -1
    let resObj = args[0]
    for(let i =0;i<N;i++){
        resObj = resObj.args[0]
    }
    return resObj
}

Вы можете протестировать его напрямую, используя:

Icshuy.md.png

🖖 полный код

Вставьте полный код, вы можете скопировать его и играть на консоли, я думаю, вы что-то выиграете! !

Function.prototype.after = function(fn) {
  const self = this
  return function(...args) {
    let result = self.apply(null, args)
    return fn.call(null,result)
  }
}
const compose = function(...args) {
  if (args.length) {
    return args.reverse().reduce(function(f1, f2) {
      return f1.after(f2)
    })
  }
}
const getCountStepAttr = function(args,N){
    // 获取前 N 步的入参;
    N = N -1
    let resObj = args[0]
    for(let i =0;i<N;i++){
        resObj = resObj.args[0]
    }
    return resObj
}
function init(...args){
    console.log("【在 init 中调用原始传参】:",getCountStepAttr(args,1))
    return {args:args,init1:"init1",init:"init"}
}
function step1(...args){
    return {args:args,step1:"step1"}
}
function step2(...args){
    return {args:args,step2:"param-step2",step2Add:"param-step2-add"}
}
function step3(...args){
    console.log("【在 step3 中调用 step2 的传参】:",getCountStepAttr(args,1).step2 , getCountStepAttr(args,1).step2Add)
    console.log("【在 step3 中调用 init 的传参】:",getCountStepAttr(args,3).init , getCountStepAttr(args,3).init1)
    console.log("【在 step3 中调用原始传参】:",getCountStepAttr(args,4))
    return {args:args,step3:"step3"}
}
compose(step3,step2,step1,init)("start")

🐵Сводный прогноз

О чем эта статья?

На самом деле, это все еще пять золотых знаков:функциональное программирование.

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

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

Мы используем ввод и вывод функции, чтобы выразить отношение отображения, используем имя функции, чтобы выразить реализацию функции внутри функции, используем передачу параметров, чтобы выразить бизнес-логику, и используем среду с закрытой областью для создания чистого кода~

Конечно, у вас может быть много хороших идей,Чистый путь к кодуДо этого еще далеко! Горы встают, и пейзаж останавливается, хотя я не могу до него дотянуться, мое сердце тоскует по нему. Кроме того, действительно не уверен, может ли это быть "к"!

Если вы видите все это здесь, почему бы не поставить лайк 👍👍👍 Писать нелегко, спасибо за вашу поддержку 👏👏👏

Добро пожаловать лайк, избранное, комментарий~

Я Энтони Наггетс, официальная учетная запись имеет то же имя, вывод раскрывает ввод, техническое понимание жизни, до свидания~