🐛 Гнездование гусениц
Я думаю, вы наверняка видели такой код:
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{
...
}
Честно говоря, не то, чтобы этот код был плохим, но каждый раз, когда я его вижу, он будет вызывать у Бенгуа дискомфорт.
Такое ощущение, что это гусеница. . .
Для того, чтобы выразить это в образе, Бенгуа искренне приглашает художников душиОхранник ЭнтониСделай принципиальную схему, салют! ! ( ̄︶ ̄)↗
* Источник изображения: Хранитель Энтони, воспроизводится по желанию без разрешения.
Такое письмо всегда будет сопровождаться различными логическими суждениями, неявным вводом-выводом, я действительно не смею его трогать, боюсь, что оно "умрет" на глазах у вас!
🌴Цепочка ответственности Бамбук
Объектив поворачивается к [Цепи ответственности], которая является одним из 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)
По общему смыслу он похож на бамбук? Каждый раздел (функция ввода, вывода) особенно нагляден. Дело в том, что он развязан и его очень легко собрать~
* Источник изображения: Хранитель Энтони, воспроизводится по желанию без разрешения.
основной, код для создания цепочки выглядит следующим образом:
Функция 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 продолжает расти.
если мы используемflat(Infinity)
Сгладьте массив, и передача параметров станет такой:
С этим есть большая проблема: нужно сравнивать порядок передачи параметров в массиве! Это очень хлопотно, потому что нет уверенности в том, что параметры процесса будут добавлены, удалены или изменены в один прекрасный день.
Поэтому ожидается, что его можно заменить объектом в качестве параметра,Избавьтесь от оков передачи параметров, чтобы. Например:
{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")
получить:
Очевидно, это не то, что нам нужно, мы должны продолжать печатать, чтобы найти шаблон:
вау~
существуетstep3
хочу получитьstep1
, нужно 2.args[0]
;
существуетstep2
хочу получитьstep1
, только 1.args[0]
;
В принципе, мы можем придумать:Чтобы получить параметры первых 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
}
Вы можете протестировать его напрямую, используя:
🖖 полный код
Вставьте полный код, вы можете скопировать его и играть на консоли, я думаю, вы что-то выиграете! !
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")
🐵Сводный прогноз
О чем эта статья?
На самом деле, это все еще пять золотых знаков:функциональное программирование.
В процессе мы инкапсулируем императивный код с помощью простых чистых функций и, наконец, объединяем их в различные богатые функции.
В этом процессе вы можете разбирать, добавлять дополнения или реорганизовывать дизайн по своему желанию, и вам действительно не нужно слишком беспокоиться о скрытых логических ошибках или сложных делах, вызванных сцеплением!
Мы используем ввод и вывод функции, чтобы выразить отношение отображения, используем имя функции, чтобы выразить реализацию функции внутри функции, используем передачу параметров, чтобы выразить бизнес-логику, и используем среду с закрытой областью для создания чистого кода~
Конечно, у вас может быть много хороших идей,Чистый путь к кодуДо этого еще далеко! Горы встают, и пейзаж останавливается, хотя я не могу до него дотянуться, мое сердце тоскует по нему. Кроме того, действительно не уверен, может ли это быть "к"!
Если вы видите все это здесь, почему бы не поставить лайк 👍👍👍 Писать нелегко, спасибо за вашу поддержку 👏👏👏
Добро пожаловать лайк, избранное, комментарий~
Я Энтони Наггетс, официальная учетная запись имеет то же имя, вывод раскрывает ввод, техническое понимание жизни, до свидания~