JavaScript で複雑な判断を記述するエレガントな方法

coldplay.xixi
リリース: 2020-06-16 16:56:42
転載
2558 人が閲覧しました

JavaScript で複雑な判断を記述するエレガントな方法

前提

JSコードを書く際、複雑な論理判断に遭遇することがよくありますが、通常はif/elseやswitchを使って複数の条件判断を実装できますが、これには問題があります。ロジックが複雑になるにつれて、コード内の if/else/switch はますます肥大化し、理解しにくくなります。そこで、判定ロジックをよりエレガントに記述するにはどうすればよいでしょうか?この記事で一度試してみましょう。

最初にコードを見てください

/**
 * 按钮点击事件
 * @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
 */const onButtonClick = (status)=>{  if(status == 1){
    sendLog('processing')
    jumpTo('IndexPage')
  }else if(status == 2){
    sendLog('fail')
    jumpTo('FailPage')
  }else if(status == 3){
    sendLog('fail')
    jumpTo('FailPage')
  }else if(status == 4){
    sendLog('success')
    jumpTo('SuccessPage')
  }else if(status == 5){
    sendLog('cancel')
    jumpTo('CancelPage')
  }else {
    sendLog('other')
    jumpTo('Index')
  }
}复制代码
ログイン後にコピー

コードを通して、このボタンのクリック ロジックを確認できます。さまざまなアクティビティの状態に応じて 2 つのことを実行します。ログを送信してポイントを埋め、対応するページにジャンプします。このコードの書き換え計画を簡単に提案できます。スイッチが表示されます:

/**
 * 按钮点击事件
 * @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
 */const onButtonClick = (status)=>{  switch (status){    case 1:
      sendLog('processing')
      jumpTo('IndexPage')      break
    case 2:    case 3:
      sendLog('fail')
      jumpTo('FailPage')      break  
    case 4:
      sendLog('success')
      jumpTo('SuccessPage')      break
    case 5:
      sendLog('cancel')
      jumpTo('CancelPage')      break
    default:
      sendLog('other')
      jumpTo('Index')      break
  }
}复制代码
ログイン後にコピー

まあ、これは if/else よりもはるかに明確に見えます。慎重な学生は、ちょっとしたヒント、ケース 2 とケース 3 のロジックが同じ場合、実行文を省略してブレークすると、ケース 2 でケース 3 のロジックが自動的に実行されます。

このとき、学生の中には、もっと簡単に書く方法があると言う人もいます:

const actions = {  '1': ['processing','IndexPage'],  '2': ['fail','FailPage'],  '3': ['fail','FailPage'],  '4': ['success','SuccessPage'],  '5': ['cancel','CancelPage'],  'default': ['other','Index'],
}/**
 * 按钮点击事件
 * @param {number} status 活动状态:1开团进行中 2开团失败 3 商品售罄 4 开团成功 5 系统取消
 */const onButtonClick = (status)=>{  let action = actions[status] || actions['default'],
      logName = action[0],
      pageName = action[1]
  sendLog(logName)
  jumpTo(pageName)
}复制代码
ログイン後にコピー

上記のコードは見た目がすっきりしていますが、この方法の賢い点は、判定条件を使用していることです。オブジェクトとして 属性名は処理ロジックをオブジェクトの属性値として使用し、ボタンをクリックするとオブジェクトの属性を検索して論理的に判定するため、特に単項の条件判定に適した記述方法です。

他の書き方はありますか?一部:

const actions = new Map([
  [1, ['processing','IndexPage']],
  [2, ['fail','FailPage']],
  [3, ['fail','FailPage']],
  [4, ['success','SuccessPage']],
  [5, ['cancel','CancelPage']],
  ['default', ['other','Index']]
])/**
 * 按钮点击事件
 * @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
 */const onButtonClick = (status)=>{  let action = actions.get(status) || actions.get('default')
  sendLog(action[0])
  jumpTo(action[1])
}复制代码
ログイン後にコピー

このように書くと、es6 では Map オブジェクトが使用されます。 Map オブジェクトと Object オブジェクトの違いは何ですか?

  1. オブジェクトには通常、独自のプロトタイプがあるため、オブジェクトには常に「プロトタイプ」キーがあります。
  2. オブジェクトのキーは文字列またはシンボルのみですが、マップのキーは任意の値にすることができます。
  3. マップのキーと値のペアの数は size 属性を通じて簡単に取得できますが、オブジェクトのキーと値のペアの数は手動でのみ確認できます。

問題をアップグレードする必要があります。以前は、ボタンをクリックしたときのステータスを判断するだけで済みました。今では、ユーザーの ID も判断する必要があります:

/**
 * 按钮点击事件
 * @param {number} status 活动状态:1开团进行中 2开团失败 3 开团成功 4 商品售罄 5 有库存未开团
 * @param {string} identity 身份标识:guest客态 master主态
 */const onButtonClick = (status,identity)=>{  if(identity == 'guest'){    if(status == 1){      //do sth
    }else if(status == 2){      //do sth
    }else if(status == 3){      //do sth
    }else if(status == 4){      //do sth
    }else if(status == 5){      //do sth
    }else {      //do sth
    }
  }else if(identity == 'master') {    if(status == 1){      //do sth
    }else if(status == 2){      //do sth
    }else if(status == 3){      //do sth
    }else if(status == 4){      //do sth
    }else if(status == 5){      //do sth
    }else {      //do sth
    }
  }
}复制代码
ログイン後にコピー

Forgiveコードが冗長すぎるため、各判断の具体的な詳細を書かなかったのは私です。

また if/else を使用することをご容赦ください。論理的判断のこのような大規模なセクションを記述するために、今でも if/else を使用している人がたくさんいるためです。

上記の例を見ると、ロジックを二項判定にバージョンアップすると判定量が2倍になり、コード量も2倍になることがわかりますが、このときどうすればよりスッキリと書けるでしょうか?

const actions = new Map([
  ['guest_1', ()=>{/*do sth*/}],
  ['guest_2', ()=>{/*do sth*/}],
  ['guest_3', ()=>{/*do sth*/}],
  ['guest_4', ()=>{/*do sth*/}],
  ['guest_5', ()=>{/*do sth*/}],
  ['master_1', ()=>{/*do sth*/}],
  ['master_2', ()=>{/*do sth*/}],
  ['master_3', ()=>{/*do sth*/}],
  ['master_4', ()=>{/*do sth*/}],
  ['master_5', ()=>{/*do sth*/}],
  ['default', ()=>{/*do sth*/}],
])/**
 * 按钮点击事件
 * @param {string} identity 身份标识:guest客态 master主态
 * @param {number} status 活动状态:1 开团进行中 2 开团失败 3 开团成功 4 商品售罄 5 有库存未开团
 */const onButtonClick = (identity,status)=>{  let action = actions.get(`${identity}_${status}`) || actions.get('default')
  action.call(this)
}复制代码
ログイン後にコピー

上記のコードの核となるロジックは、2 つの条件を 1 つの文字列に結合し、条件文字列をキーとして、処理関数を値として使用して Map オブジェクトを検索および実行するというものです。この記述方法は次のとおりです。複数で使用されます。特に条件判断をする場合に便利です。

もちろん、上記のコードは、Object オブジェクトを使用して実装されている場合も同様です。

const actions = {  'guest_1':()=>{/*do sth*/},  'guest_2':()=>{/*do sth*/},  //....}const onButtonClick = (identity,status)=>{  let action = actions[`${identity}_${status}`] || actions['default']
  action.call(this)
}复制代码
ログイン後にコピー

クエリ条件を文字列に記述するのが少し面倒だと感じる学生がいる場合は、次のコードがあります。別の解決策は、Map オブジェクトを使用し、Object オブジェクトをキーとして使用することです:

const actions = new Map([
  [{identity:'guest',status:1},()=>{/*do sth*/}],
  [{identity:'guest',status:2},()=>{/*do sth*/}],  //...])const onButtonClick = (identity,status)=>{  let action = [...actions].filter(([key,value])=>(key.identity == identity && key.status == status))
  action.forEach(([key,value])=>value.call(this))
}复制代码
ログイン後にコピー

これはもう少し高度ですか?

Map と Object の違いは、ここでも参照できます。Map は、任意のタイプのデータをキーとして使用できます。

ここで、難易度を少しアップグレードします。ゲストの場合、ステータス 1 ~ 4 の処理ロジックが同じである場合はどうなるでしょうか。最悪のケースは次のとおりです。

const actions = new Map([
  [{identity:'guest',status:1},()=>{/* functionA */}],
  [{identity:'guest',status:2},()=>{/* functionA */}],
  [{identity:'guest',status:3},()=>{/* functionA */}],
  [{identity:'guest',status:4},()=>{/* functionA */}],
  [{identity:'guest',status:5},()=>{/* functionB */}],  //...])复制代码
ログイン後にコピー

A better way to処理ロジック関数をキャッシュする:

const actions = ()=>{  const functionA = ()=>{/*do sth*/}  const functionB = ()=>{/*do sth*/}  return new Map([
    [{identity:'guest',status:1},functionA],
    [{identity:'guest',status:2},functionA],
    [{identity:'guest',status:3},functionA],
    [{identity:'guest',status:4},functionA],
    [{identity:'guest',status:5},functionB],    //...
  ])
}const onButtonClick = (identity,status)=>{  let action = [...actions()].filter(([key,value])=>(key.identity == identity && key.status == status))
  action.forEach(([key,value])=>value.call(this))
}复制代码
ログイン後にコピー

このように書けば日常的なニーズは満たせますが、正直なところ、上記の functionA を 4 回書き直すのはまだ少し気がかりです。 , たとえば、identity には 3 つの状態があり、status には 10 の状態があります。その場合、30 の処理ロジックを定義する必要がありますが、これらのロジックの多くは同じであることがよくあります。これは私が受け入れたくないことのようです。

const actions = ()=>{  const functionA = ()=>{/*do sth*/}  const functionB = ()=>{/*do sth*/}  return new Map([
    [/^guest_[1-4]$/,functionA],
    [/^guest_5$/,functionB],    //...
  ])
}const onButtonClick = (identity,status)=>{  let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`)))
  action.forEach(([key,value])=>value.call(this))
}复制代码
ログイン後にコピー

ここでは Map の利点がより際立っています。通常の型をキーとして使用できるため、無限の可能性が広がります。需要が変化した場合は、すべてのデータに対してログ埋め込みポイントを送信する必要があります。ゲストの状況やステータスの状況によっても個別の論理処理が必要になるため、次のように書くことができます:

const actions = ()=>{  const functionA = ()=>{/*do sth*/}  const functionB = ()=>{/*do sth*/}  const functionC = ()=>{/*send log*/}  return new Map([
    [/^guest_[1-4]$/,functionA],
    [/^guest_5$/,functionB],
    [/^guest_.*$/,functionC],    //...
  ])
}const onButtonClick = (identity,status)=>{  let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`)))
  action.forEach(([key,value])=>value.call(this))
}复制代码
ログイン後にコピー

つまり、配列ループの特性を利用して、規則的な条件を満たすロジックが実行されます。公開ロジックと個別ロジックを同時に実行することができます。規則的なルールが存在するため、想像力を広げることができます。より多くの遊び方を開くために、この記事では詳しく説明しません。

概要

この記事では、次のような論理的な判断を記述する 8 つの方法を説明しました。

  1. if/else
  2. switch
  3. 1元判定の場合:Objectに保存
  4. 一方判定の場合:Mapに保存
  5. 複数判定の場合:条件を文字列に繋いで保存in Object
  6. 複数判定の場合:条件を文字列に連結してマップに保存
  7. 複数判定の場合:条件をオブジェクトとして保存してマップに保存
  8. 複数の判定を行う場合:条件を正規表現で記述しマップに保存する

この記事はこれで終了となります。 if/else/switch があります。

この記事に興味がある場合は、著者の WeChat 公開アカウント「大 Zhuan Zhuan Fe」をフォローしてください。

推奨チュートリアル:「 JavaScript基本チュートリアル

以上がJavaScript で複雑な判断を記述するエレガントな方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:juejin.im
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート