我应该如何处理嵌套条件语句?

时间:2019-02-21 15:47:03

标签: javascript if-statement

我有一系列嵌套的条件检查,我试图提出一个干净的解决方案,并尽可能避免if{}else{}地狱。请参阅示例代码以说明我要实现的目标。

总而言之,我有两个对象CACHE_FILE_AGE_LIMITCACHE_FILE_CURRENT_AGE,并且我正在尝试进行嵌套的年龄检查:

first check the days,
    then check hours,
       then check minutes,
           then check seconds

这个想法是,当我们达到更大的值(例如天数相等)时,我们继续使用小时,如果我们在那获得了更大的小时值,我们将结束检查。

有关更多信息,请参见示例。

const CACHE_FILE_AGE_LIMIT = {
  days: 3,
  hours: 6,
  minutes: 15,
  seconds: 57
}

const CACHE_FILE_CURRENT_AGE = {
  days: 3,
  hours: 5,
  minutes: 14,
  seconds: 57
}

function timeDiff(ageObj1, ageObj2) {
  console.log('days_limit', ageObj1.days, '?', 'days_age', ageObj2.days);
  // old: days
  document.getElementById("current_days").innerHTML = ageObj2.days;
  if (ageObj1.days < ageObj2.days) {
    console.log('old (days)');
    document.getElementById("current_days").classList.add("red");
    return true;
  } else {
  	// equal: days
    if (ageObj1.days == ageObj2.days) {
    	document.getElementById("current_hours").innerHTML = ageObj2.hours;
      console.log('\tnext: hours');
      console.log('\thours_limit', ageObj1.hours, '?', 'hours_age', ageObj2.hours);
      if (ageObj1.hours < ageObj2.hours) {
        console.log('\told (hours)');
        document.getElementById("current_hours").classList.add("red");
        return true;
      } else {
        // equal: hours
        if (ageObj1.hours == ageObj2.hours) {
          document.getElementById("current_minutes").innerHTML = ageObj2.minutes;
          console.log('\t\tnext (minutes)');
          console.log('\t\tminutes_limit', ageObj1.minutes, '?', 'minutes_age', ageObj2.minutes);
          if (ageObj1.minutes < ageObj2.minutes) {
            // old: minutes
            console.log('\t\told (minutes)');
            document.getElementById("current_minutes").classList.add("red");
            return true;
          } else {
            // equal: minutes
            if (ageObj1.minutes == ageObj2.minutes) {
              document.getElementById("current_seconds").innerHTML = ageObj2.seconds;
              console.log('\t\t\tnext (seconds)');
              console.log('\t\t\tseconds_limit', ageObj1.seconds, '?', 'seconds_age', ageObj2.seconds);
              if (ageObj1.seconds < ageObj2.seconds) {
                console.log('\t\t\told (seconds)');
                document.getElementById("current_seconds").classList.add("red");
                return true;
              } else {
                console.log('\t\t\tNOT old (seconds)');
                document.getElementById("current_seconds").classList.add("blue");
                return false;
              }
            } else {
              console.log('\t\tNOT old (minutes)');
              return false;
            }
          }
        } else {
          console.log('\tNOT old (hours)');
          document.getElementById("current_hours").classList.add("blue");
          return false;
        }
      }
    } else {
      console.log('NOT old (days)');
      document.getElementById("current_days").classList.add("blue");
      return false;
    }
  }
}

// Populate Limits
var limit_fields = document.querySelectorAll(".limit");
for(i=0; i < limit_fields.length; i++){
				//console.log('--->', i)
        let id = limit_fields[i].id.split("_")[1];
        let val = CACHE_FILE_AGE_LIMIT[id]
        //console.log('\tid:', id, 'val:', val);
        limit_fields[i].innerHTML = val;
    }

// Evaluate Age
document.getElementById("output").innerHTML = timeDiff(CACHE_FILE_AGE_LIMIT, CACHE_FILE_CURRENT_AGE) ? "old" : "up to date";
.tg {
  border-collapse: collapse;
  border-spacing: 0;
}

.tg td {
  font-family: Arial, sans-serif;
  font-size: 12px;
  padding: 10px 5px;
  border-style: solid;
  border-width: 1px;
  overflow: hidden;
  word-break: normal;
  border-color: black;
}

.tg th {
  font-family: Arial, sans-serif;
  font-size: 12px;
  font-weight: normal;
  padding: 10px 5px;
  border-style: solid;
  border-width: 2px;
  overflow: hidden;
  border-color: black;
}

.tg .value {
  color: blue;
  text-align: center;
  vertical-align: top;
}

.tg .current {
  color: blue;
  text-align: center;
  vertical-align: top;
}

.tg .current.red {
  color: red;
}

.tg .current.blue {
  color: blue;
}

.tg .limit {
  color: #85929E;
  text-align: center;
  vertical-align: top;
}

.tg .header {
  background-color: #ffffc7;
  text-align: center;
  vertical-align: top;
}

.tg .item {
  background-color: #ffffc7;
  font-style: italic;
  text-align: right;
  vertical-align: top;
}

.tg .empty {
  background-color: #9b9b9b;
  text-align: right;
  vertical-align: top;
}

.tg .result {
  font-weight: bold;
  font-style: italic;
  background-color: #ffce93;
  text-align: right;
  vertical-align: top;
}

.tg .output {
  background-color: #FDEBD0;
  text-align: center;
  vertical-align: top;
}
<table class="tg" style="undefined;table-layout: fixed; width: 265px">
  <colgroup>
    <col style="width: 92px">
    <col style="width: 92px">
    <col style="width: 92px">
  </colgroup>
  <tr>
    <th class="empty"></th>
    <th class="header">CURRENT</th>
    <th class="header">LIMITS</th>
  </tr>
  <tr>
    <td class="item">DAYS</td>
    <td id="current_days" class="value current">-</td>
    <td id="limit_days" class="value limit"></td>
  </tr>
  <tr>
    <td class="item">HOURS</td>
    <td id="current_hours" class="value current">-</td>
    <td id="limit_hours" class="value limit"></td>
  </tr>
  <tr>
    <td class="item">MINUTES</td>
    <td id="current_minutes" class="value current">-</td>
    <td id="limit_minutes" class="value limit"></td>
  </tr>
  <tr>
    <td class="item">SECONDS</td>
    <td id="current_seconds" class="value current">-</td>
    <td id="limit_seconds" class="value limit"></td>
  </tr>
  <tr>
    <td class="result">RESULT</td>
    <td id="output" class="output" colspan="2">up to date</td>
  </tr>
</table>

如您所见,一旦很清楚hours足以确定年龄,则条件执行结束。

请更改CACHE_FILE_CURRENT_AGE中的值以对其进行测试。

---更新---

有多种解决方案,因此感谢大家的帮助。不幸的是,我将不得不选择答案之一来结束问题。

这是解决方案的演示:

enter image description here

2 个答案:

答案 0 :(得分:1)

因此,另一种可能性是创建类似UNITS数组的对象,该数组仅包含您的时事对象的每个属性的文本表示形式,以您想要处理它们的顺序。然后,只需使用while循环迭代该UNITS数组,然后依次更新每个DOM元素。

const CACHE_FILE_AGE_LIMIT = {
  days: 3,
  hours: 6,
  minutes: 15,
  seconds: 57
}, CACHE_FILE_CURRENT_AGE = {
  days: 3,
  hours: 5,
  minutes: 17, 
  seconds: 57
  
}, UNITS = ["days", "hours","minutes","seconds"]

function timeDiff(ageObj1, ageObj2) {
  // We create a flag, and an index to iterate over our UNITS array
  let unitsIndex = 0;
  
  // if the upToDate flag is TRUE
  while(unitsIndex <= UNITS.length){
    // We'll use template literals to create the IDs
    currentEl = document.getElementById(`current_${UNITS[unitsIndex]}`),
    limitEl = document.getElementById(`limit_${UNITS[unitsIndex]}`);

    console.log(UNITS[unitsIndex],ageObj1[UNITS[unitsIndex]],ageObj2[UNITS[unitsIndex]]) 
    // Update the content of the current and limit elements    
    currentEl.innerHTML = ageObj2[UNITS[unitsIndex]];
    limitEl.innerHTML = ageObj1[UNITS[unitsIndex]];
    
    // Here we check: is our limit unit less than our current?
    if (ageObj1[UNITS[unitsIndex]] < ageObj2[UNITS[unitsIndex]]) {
      /**
       * In this case, our current has exceeded our limit. Bad bad bad.
       **/
       console.info(`The current ${UNITS[unitsIndex]} is stale.`)
      currentEl.classList.add("red");  
      return false;
    } else if(ageObj1[UNITS[unitsIndex]] > ageObj2[UNITS[unitsIndex]]){
      /**
       * In this case, our limit is  more than a full unit greater than our current. goodgoodgood.
       **/
       console.info(`The current ${UNITS[unitsIndex]} is more than a full unit to the good.`)
      return true;
    }
         
    //increment our UNITS array pointer
    unitsIndex++;
  }
  // if we get here, then all the DHMS have passed --  we can return true
  return true;
}

// Populate Limits
var limit_fields = document.querySelectorAll(".limit");
for(i=0; i < limit_fields.length; i++){
				//console.log('--->', i)
        let id = limit_fields[i].id.split("_")[1];
        let val = CACHE_FILE_AGE_LIMIT[id]
        //console.log('\tid:', id, 'val:', val);
        limit_fields[i].innerHTML = val;
    }

// Evaluate Age
document.getElementById("output").innerHTML = timeDiff(CACHE_FILE_AGE_LIMIT, CACHE_FILE_CURRENT_AGE) ? "up to date" : "old";
.tg {
  border-collapse: collapse;
  border-spacing: 0;
}

.tg td {
  font-family: Arial, sans-serif;
  font-size: 12px;
  padding: 10px 5px;
  border-style: solid;
  border-width: 1px;
  overflow: hidden;
  word-break: normal;
  border-color: black;
}

.tg th {
  font-family: Arial, sans-serif;
  font-size: 12px;
  font-weight: normal;
  padding: 10px 5px;
  border-style: solid;
  border-width: 2px;
  overflow: hidden;
  border-color: black;
}

.tg .value {
  color: blue;
  text-align: center;
  vertical-align: top;
}

.tg .current {
  color: blue;
  text-align: center;
  vertical-align: top;
}

.tg .current.red {
  color: red;
}

.tg .current.blue {
  color: blue;
}

.tg .limit {
  color: #85929E;
  text-align: center;
  vertical-align: top;
}

.tg .header {
  background-color: #ffffc7;
  text-align: center;
  vertical-align: top;
}

.tg .item {
  background-color: #ffffc7;
  font-style: italic;
  text-align: right;
  vertical-align: top;
}

.tg .empty {
  background-color: #9b9b9b;
  text-align: right;
  vertical-align: top;
}

.tg .result {
  font-weight: bold;
  font-style: italic;
  background-color: #ffce93;
  text-align: right;
  vertical-align: top;
}

.tg .output {
  background-color: #FDEBD0;
  text-align: center;
  vertical-align: top;
}
<table class="tg" style="undefined;table-layout: fixed; width: 265px">
  <colgroup>
    <col style="width: 92px">
    <col style="width: 92px">
    <col style="width: 92px">
  </colgroup>
  <tr>
    <th class="empty"></th>
    <th class="header">CURRENT</th>
    <th class="header">LIMITS</th>
  </tr>
  <tr>
    <td class="item">DAYS</td>
    <td id="current_days" class="value current">-</td>
    <td id="limit_days" class="value limit"></td>
  </tr>
  <tr>
    <td class="item">HOURS</td>
    <td id="current_hours" class="value current">-</td>
    <td id="limit_hours" class="value limit"></td>
  </tr>
  <tr>
    <td class="item">MINUTES</td>
    <td id="current_minutes" class="value current">-</td>
    <td id="limit_minutes" class="value limit"></td>
  </tr>
  <tr>
    <td class="item">SECONDS</td>
    <td id="current_seconds" class="value current">-</td>
    <td id="limit_seconds" class="value limit"></td>
  </tr>
  <tr>
    <td class="result">RESULT</td>
    <td id="output" class="output" colspan="2">up to date</td>
  </tr>
</table>

请注意,我正在使用模板文字来定义每次迭代的ID:

`current_${UNITS[unitIndex] }`

By doing so, I don't have to hard-code the id every time. As long as I can trust the prefixes, this works pretty well. And it seems to get rid of a lot of your if statements, simply by running them each sequentially.

EDIT: I'm an idiot. So there are two branches to your if statement (well, three). First, if the current is beyond the limit, return false immediately, as that is OLD. Second, if the limit is more than a full unit greater than the current, immediately return true, as the current is well within its limit. Third, if they are equal, loop on to the next unit. If all units process and we fall through the while loop, then  they are the same exact. Weird, but still good. Code updated to reflect the SECOND case.

答案 1 :(得分:0)

将嵌套条件转换为代码的线性表示的唯一方法是将单个条件的微粒移动到函数中,并使用some导致短路退出运行。在需要返回值的同时,需要存储此标志,因为some的返回值对此无效。

这种方法的优点是添加或多或少的无限条件,根据需要打破它们并返回布尔值以进行进一步的操作。

缺点是,您需要查找数据,嵌套结构和理解,即不管标志的值如何,都将调用下一个条件/函数,直到函数返回true为止。

const CACHE_FILE_AGE_LIMIT = {
  days: 3,
  hours: 6,
  minutes: 15,
  seconds: 57
}

const CACHE_FILE_CURRENT_AGE = {
  days: 3,
  hours: 5,
  minutes: 14,
  seconds: 57
}

function timeDiff(ageObj1, ageObj2) {
    var flag;
    [
        () => {
            document.getElementById("current_days").innerHTML = ageObj2.days;
            if (ageObj1.days < ageObj2.days) {
                document.getElementById("current_days").classList.add("red");
                return flag = true;
            }
        },
        () => {
            if (ageObj1.days == ageObj2.days) {
                document.getElementById("current_hours").innerHTML = ageObj2.hours;
            } else {
                document.getElementById("current_days").classList.add("blue");
                flag = false;
                return true;
            }
        },
        () => {
            if (ageObj1.hours < ageObj2.hours) {
                document.getElementById("current_hours").classList.add("red");
                return flag = true;
            }
        },
        () => {
            if (ageObj1.hours == ageObj2.hours) {
                document.getElementById("current_minutes").innerHTML = ageObj2.minutes;
            } else {
                document.getElementById("current_hours").classList.add("blue");
                flag = false;
                return true;
            }
        },
        () => {
            if (ageObj1.minutes < ageObj2.minutes) {
                document.getElementById("current_minutes").classList.add("red");
                return flag = true;
            }
        },
        () => {
            if (ageObj1.minutes == ageObj2.minutes) {
                document.getElementById("current_seconds").innerHTML = ageObj2.seconds;
            } else {
                flag = false;
                return true;
            }
        },
        () => {
            if (ageObj1.seconds < ageObj2.seconds) {
                document.getElementById("current_seconds").classList.add("red");
                return flag = true;
            } else {
                document.getElementById("current_seconds").classList.add("blue");
                flag = false;
                return true;
            }
        }
    ].some(fn => fn());
    return flag;
}


// Populate Limits
var limit_fields = document.querySelectorAll(".limit");
for(i=0; i < limit_fields.length; i++){
				//console.log('--->', i)
        let id = limit_fields[i].id.split("_")[1];
        let val = CACHE_FILE_AGE_LIMIT[id]
        //console.log('\tid:', id, 'val:', val);
        limit_fields[i].innerHTML = val;
    }

// Evaluate Age
document.getElementById("output").innerHTML = timeDiff(CACHE_FILE_AGE_LIMIT, CACHE_FILE_CURRENT_AGE) ? "old" : "up to date";
.tg {
  border-collapse: collapse;
  border-spacing: 0;
}

.tg td {
  font-family: Arial, sans-serif;
  font-size: 12px;
  padding: 10px 5px;
  border-style: solid;
  border-width: 1px;
  overflow: hidden;
  word-break: normal;
  border-color: black;
}

.tg th {
  font-family: Arial, sans-serif;
  font-size: 12px;
  font-weight: normal;
  padding: 10px 5px;
  border-style: solid;
  border-width: 2px;
  overflow: hidden;
  border-color: black;
}

.tg .value {
  color: blue;
  text-align: center;
  vertical-align: top;
}

.tg .current {
  color: blue;
  text-align: center;
  vertical-align: top;
}

.tg .current.red {
  color: red;
}

.tg .current.blue {
  color: blue;
}

.tg .limit {
  color: #85929E;
  text-align: center;
  vertical-align: top;
}

.tg .header {
  background-color: #ffffc7;
  text-align: center;
  vertical-align: top;
}

.tg .item {
  background-color: #ffffc7;
  font-style: italic;
  text-align: right;
  vertical-align: top;
}

.tg .empty {
  background-color: #9b9b9b;
  text-align: right;
  vertical-align: top;
}

.tg .result {
  font-weight: bold;
  font-style: italic;
  background-color: #ffce93;
  text-align: right;
  vertical-align: top;
}

.tg .output {
  background-color: #FDEBD0;
  text-align: center;
  vertical-align: top;
}
<table class="tg" style="undefined;table-layout: fixed; width: 265px">
  <colgroup>
    <col style="width: 92px">
    <col style="width: 92px">
    <col style="width: 92px">
  </colgroup>
  <tr>
    <th class="empty"></th>
    <th class="header">CURRENT</th>
    <th class="header">LIMITS</th>
  </tr>
  <tr>
    <td class="item">DAYS</td>
    <td id="current_days" class="value current">-</td>
    <td id="limit_days" class="value limit"></td>
  </tr>
  <tr>
    <td class="item">HOURS</td>
    <td id="current_hours" class="value current">-</td>
    <td id="limit_hours" class="value limit"></td>
  </tr>
  <tr>
    <td class="item">MINUTES</td>
    <td id="current_minutes" class="value current">-</td>
    <td id="limit_minutes" class="value limit"></td>
  </tr>
  <tr>
    <td class="item">SECONDS</td>
    <td id="current_seconds" class="value current">-</td>
    <td id="limit_seconds" class="value limit"></td>
  </tr>
  <tr>
    <td class="result">RESULT</td>
    <td id="output" class="output" colspan="2">up to date</td>
  </tr>
</table>

但是您可以使用包含键的数组并执行,直到某些部分返回true

function timeDiff(ageObj1, ageObj2) {
    var flag;

    ['days', 'hours', 'minutes', 'seconds'].some((key, index, keys) => {
        var nextKey = keys[index + 1],
            element = document.getElementById("current_" + key),
            nextElement = document.getElementById("current_" + nextKey);

        element.innerHTML = ageObj2[key];
        if (ageObj1[key] < ageObj2[key]) {
            element.classList.add("red");
            return flag = true;
        }
        if (ageObj1[key] == ageObj2[key] && nextKey) {
            nextElement.innerHTML = ageObj2[nextKey];
        } else {
            element.classList.add("blue");
            flag = false;
            return true;
        }
    });

    return flag;
}