JavaScript函數式編程之管道分支,消弭if/else的一種體式格局

以下代碼會用到函數組合函數compose,只需曉得compose是干什麼的就足夠了,假如獵奇詳細的完成,能夠看《JavaScript函數式編程之函數組合函數compose和pipe的完成》

管道是函數式編程中常常運用的,許多時刻我們須要依據前提推斷舉行組合函數的挑選,簡樸的說就是從本來的一條管道變成兩條管道,依據推斷挑選進入哪一條。

這裏的關鍵在於,我們怎樣推斷上一個函數的返回值應當進入哪一條管道?

let step1 = x => x ? 1 : 2;
let step2 = x => x === 1 ? 3 : 4;
let step3 = x => x === 3 ? 5 : 6;
let getResult = compose(step3, step2, step1)
let result = getResult(1);

這是最直接的要領,每一步依據返回值零丁推斷,假如step1的返回值發生了變化,下一步的推斷也須要隨着修正,這類寫法明顯不好。那我們能不能在返回值的基礎上加上一個標識,特地用來做推斷?我們很天然的就會想到對象。

let step1 = x => {
  if (x) {
    return {
      value: 1,
      identity: 'channelOne'
    }
  } else {
    return {
      value: 2,
      identity: 'channelTwo'
    }
  }
}
let step2 = x => {
  if (x.identity === 'channelOne') {
    return x.value = 3;
  } else {
    return x.value = 4;
  }
}
let step3 = x => {
  if (x.identity === 'channelOne') {
    return x.value = 5;
  } else {
    return x.value = 6;
  }    
}
let getResult = compose(step3, step2, step1);
let result = getResult(1);

是否是好了許多?不過這依舊要繼承革新。當我們須要運用forin等體式格局遍歷對象時,identity會被遍歷出來,平常情況下我們都願望它不會被遍歷,那就還須要把這個屬性定義為不可枚舉的。
修正step1並簡化代碼:

let step1 = x => {
  if (x) {
    let obj = {value: 1};
    Object.defineProperty(obj, 'identity', {
      enumerable: false,
      value: 'channelOne'
    });
    return obj;
  } else {
    let obj = {value: 2};
    Object.defineProperty(obj, 'identity', {
      enumerable: false,
      value: 'channelTwo'
    });
    return obj;
  }
}
let selectChannel = (fn1, fn2) => val => val.identity === 'channelOne' ? fn1(val) : fn2(val);
let getResult = compose(
  selectChannel(
    x => Object.defineProperty(x, 'value', {value: 5}),
    x => Object.defineProperty(x, 'value', {value: 6})
  ),
  selectChannel(
    x => Object.defineProperty(x, 'value', {value: 3}),
    x => Object.defineProperty(x, 'value', {value: 4})
  ),
  step1
);
let result = getResult(1);

在selectChannel中,函數會依據傳進來的對象的標識挑選實行。至此,功用基本上完成了,可依舊不夠好,代碼不夠簡約文雅,重用也能夠繼承革新。

用組織函數做標識

let channelOne = function(x) { this.value = x; };
channelOne.of = x => new channelOne(x);
let channelTwo = function(x) { this.value = x; };
channelTwo.of = x => new channelTwo(x);
let step1 = x => x ? channelOne.of(1) : channelTwo.of(2);
let selectChannel = (fn1, fn2) => val => val.constructor === channelOne ? fn1(val) : fn2(val);
let getResult = compose(
  selectChannel(x => channelOne.of(5), x => channelTwo.of(6)),
  selectChannel(x => channelOne.of(3), x => channelTwo.of(4)),
  step1
);
let result = getResult(1);

太棒了!
看到這裏,有么有欣喜的發明,if/else不見了?肯定會有人以為我是一個換湯不換藥的市儈。雖然if/else不見了,但是我用了三元運算符,這在實質上有什麼區分?
答案是,沒區分,他們都是前提推斷,這是不可避免的。
我們無妨臨時把關注的核心放在三元運算符與if/else的區分上面來。我們什麼時刻會運用三元運算符?是前提推斷很簡樸的時刻,簡樸到只須要一個表達式,而不是龐雜的操縱。雖然三元運算符也能夠用逗號離隔表達式從而舉行多個操縱,可我們這個時刻更情願運用if/else
說到這裏就已很明顯了,這類組織函數做標識的體式格局,把龐雜的前提推斷剖析了,剖析到在做推斷的時刻只須要挑選方向,相干的操縱能夠扔到背面。

在《JavaScript函數式編程中的錯誤處理,強健代碼》文章中所用的思緒與本篇一樣,只不過在《JavaScript函數式編程中的錯誤處理,強健代碼》中能夠以為是以nullundefined作為標識,而本篇零丁製造了標識。本篇中的要領越發的通用,由於nullundefined也多是我們須要運用的值。

運用本篇的要領重寫《JavaScript函數式編程中的錯誤處理,強健代碼》中的代碼

let channelError = function(x) { this.value = x; };
channelError.of = x => new channelError(x);
let channelSuccess = function(x) { this.value = x; };
channelSuccess.of = x => new channelSuccess(x);
let security= fn => val => val.constructor === channelError? val : fn(val);
let validate1 = x => x ? channelSuccess.of(x) : channelError.of('validate1 is not passed!');
let validate2 = x => x.value ? x : channelError.of('validate2 is not passed!');
let handleError = x => {
  if (x.constructor === channelError) alert(x.value);
};
let postData = () => axios.post(...);
let getResult = compose(
  handleError,
  security(postData),
  security(validate2),
  security(validate1)
);

參考資料:

JS函數式編程指南

我在github https://github.com/zhuanyongx…

    原文作者:磚用西瓜
    原文地址: https://segmentfault.com/a/1190000014410702
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞