45

我个人喜欢三元运算符,在我看来,它们使复杂的表达式很容易消化。拿这个:

  const word = (distance === 0) ? 'a'
    : (distance === 1 && diff > 3) ? 'b'
    : (distance === 2 && diff > 5 && key.length > 5) ? 'c'
    : 'd';

但是在我们项目的 ESLINT 规则中,嵌套三元运算符是被禁止的,所以我必须摆脱上面的。

我正在尝试找出这种方法的替代方案。我真的不想把它变成一个巨大的 if / else 语句,但不知道是否还有其他选择。

4

14 回答 14

37

您的选择基本上是:

  1. if/else你不想做
  2. Aswitch结合if/else

我试图提出一个合理的查找地图选项,但它很快就变得不合理。

我会去#1,它不是那么大:

if (res.distance == 0) {
    word = 'a';
} else if (res.distance == 1 && res.difference > 3) {
    word = 'b';
} else if (res.distance == 2 && res.difference > 5 && String(res.key).length > 5) {
    word = 'c';
} else {
    word = 'd';
}

如果所有的大括号和垂直尺寸都困扰着你,没有它们它几乎和条件运算符版本一样简洁:

if (res.distance == 0) word = 'a';
else if (res.distance == 1 && res.difference > 3) word = 'b';
else if (res.distance == 2 && res.difference > 5 && String(res.key).length > 5) word = 'c';
else word = 'd';

(我不是在提倡,我从不提倡放弃括号或将语句if放在同一行,但其他人有不同的风格观点。)

在我看来,#2 更笨重,但这可能更像是一种风格评论:

word = 'd';
switch (res.distance) {
    case 0:
        word = 'a';
        break;
    case 1:
        if (res.difference > 3) {
            word = 'b';
        }
        break;
    case 2:
        if (res.difference > 5 && String(res.key).length > 5) {
            word = 'c';
        }
        break;
}

最后,我并不是在提倡这一点,您可以利用 JavaScript在B语法语言家族switch中不常见的事实:语句可以是表达式,并与源代码顺序中的开关值匹配:case

switch (true) {
    case res.distance == 0:
        word = 'a';
        break;
    case res.distance == 1 && res.difference > 3:
        word = 'b';
        break;
    case res.distance == 2 && res.difference > 5 && String(res.key).length > 5:
        word = 'c';
        break;
    default:
        word = 'd';
        break;
}

那有多丑?:-)

于 2015-08-29T18:29:20.990 回答
27

依我的口味,一个精心构造的嵌套三元组击败了所有那些凌乱的 if 和 switch:

const isFoo = res.distance === 0;
const isBar = res.distance === 1 && res.difference > 3;
const isBaz = res.distance === 2 && res.difference > 5 && String(res.key).length > 5;

const word =
  isFoo ? 'a' :
  isBar ? 'b' :
  isBaz ? 'c' :
          'd' ;
于 2016-09-02T19:04:24.160 回答
19

您可以编写一个立即调用的函数表达式以使其更具可读性:

const word = (() =>  {
  if (res.distance === 0) return 'a';
  if (res.distance === 1 && res.difference > 3) return 'b';
  if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) return 'c';
  return 'd';
})();

链接到repl

于 2017-11-22T21:26:58.247 回答
4

我们可以使用 && 和 || 等基本运算符来简化它

let obj = {}

function checkWord (res) {
      return (res.distance === 0)   && 'a'
             || (res.distance === 1 && res.difference > 3) && 'b' 
             || (res.distance === 2 && res.difference > 5  && String(res.key).length > 5) && 'c'
             || 'd';
           
}

// case 1 pass
obj.distance = 0
console.log(checkWord(obj))

// case 2 pass
obj.distance = 1
obj.difference = 4
console.log(checkWord(obj))

// case 3 pass
obj.distance = 2
obj.difference = 6
obj.key = [1,2,3,4,5,6]
console.log(checkWord(obj))

// case 4 fail all cases
obj.distance = -1
console.log(checkWord(obj))

于 2019-05-30T19:52:10.003 回答
3

如果您希望将 const 与嵌套的三元表达式一起使用,则可以将三元替换为函数表达式。

const res = { distance: 1, difference: 5 };

const branch = (condition, ifTrue, ifFalse) => condition?ifTrue:ifFalse;
const word = branch(
  res.distance === 0,    // if
  'a',                   // then
  branch(                // else
    res.distance === 1 && res.difference > 3,   // if
    'b',                                        // then
    branch(                                     // else
      res.distance === 2 && res.difference > 5,   // if
      'c',                                        // then
      'd'                                         // else
    )
  )
);

console.log(word);

或通过解构使用命名参数...

const branch2 = function(branch) {
  return branch.if ? branch.then : branch.else;
}

const fizzbuzz = function(num) {
  return branch2({
    if: num % 3 === 0 && num % 5 === 0,
    then: 'fizzbuzz',
    else: branch2({
        if: num % 3 === 0,
        then: 'fizz',
        else: branch2({
          if: num % 5 === 0,
          then: 'buzz',
          else: num
        })
      })
  });
}

console.log(
  [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16].map(
    cv => fizzbuzz(cv)
  )
);

编辑

在 python if 表达式之后对其进行建模可能会更清楚,如下所示:

const res = { distance: 1, difference: 5 };

const maybe = def => ({
  if: expr => {
    if (expr) {
      return { else: () => def };
    } else {
      return { else: els => els };
    }
  }
});
const word = maybe('a').if(res.distance === 0).else(
  maybe('b').if(res.distance === 1 && res.difference > 3).else(
    maybe('c').if(res.distance === 2 && res.difference > 5).else('d')
  )
);
console.log(word);

编辑

另一个删除嵌套 if/else 分支的编辑:

const res = { distance: 1, difference: 5 };

const makeResolvedValue = def => {
  const elseProp = () => def;
  return function value() {
    return {
      if: () => ({ else: elseProp, value })
    };
  }
};

const value = def => ({
  if: expr => {
    if (expr) {
      return { else: () => def, value: makeResolvedValue(def) };
    } else {
      return { else: els => els, value };
    }
  }
});

// with branching if needed
const word = value('a').if(res.distance === 0)
  .else(value('b').if(res.distance === 1 && res.difference > 3)
    .else(value('c').if(res.distance === 2 && res.difference > 5)
      .else('d')
    )
  );
console.log(word)

// implicit else option for clarity
const word2 = value('a').if(res.distance === 0)
  .value('b').if(res.distance === 1 && res.difference > 3)
  .value('c').if(res.distance === 2 && res.difference > 5)
  .else('d');

console.log(word2);

于 2017-07-02T19:43:59.423 回答
2

如果您所有的真实条件都评估为真实值(因此,如果强制为布尔值,问号和分号之间的值评估为 true ......)您可以让您的三元表达式返回false为虚假表达式。然后,您可以使用按位或 ( ||) 运算符链接它们以测试下一个条件,直到返回默认值的最后一个条件。

在下面的示例中,“condsXXX”数组表示评估条件的结果。“conds3rd”模拟第三个条件为真,“condsNone”模拟没有条件为真。在现实生活中的代码中,您将在赋值表达式中“内联”条件:

var conds3rd = [false, false, true];
var condsNone = [false, false, false];

var val3rd = (conds3rd[0] ? 1 : false) ||
  (conds3rd[1] ? 2 : false) ||
  (conds3rd[2] ? 3 : 4);

var valNone = (condsNone[0] ? 1 : false) ||
  (condsNone[1] ? 2 : false) ||
  (condsNone[2] ? 3 : 4);

alert(val3rd);
alert(valNone);

您的示例最终可能如下所示:

word = ((res.distance === 0) ? 'a' : false) ||
    ((res.distance === 1 && res.difference > 3) ? 'b' : false) ||
    ((res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c' : 'd';

作为旁注,我不觉得它是一个好看的代码,但它非常接近使用你渴望做的纯三​​元运算符......

于 2015-08-29T20:10:04.500 回答
2
word = (res.distance === 0) ? 'a'
: (res.distance === 1 && res.difference > 3) ? 'b'
: (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c'
: 'd';

这是一个较老的问题,但这是我将如何做到的......我将从默认情况开始,然后更改变量或根据需要将其不变地传递。

var word = 'd';
word = (res.distance === 0) ? 'a' : word;
word = (res.distance === 1 && res.difference > 3) ? 'b' : word
word = (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c' : word;
于 2017-09-12T18:41:09.413 回答
2

有时我们有(或只是喜欢)使用单行表达式或变量定义。因此,我们可以将破坏性赋值与三元运算符结合使用。例如,

曾是:

const a = props.a ?  props.a : cond2 ? 'val2.0' : 'val2.1' ;

让我们更新到:

const { a =  cond2 ? 'val2.0' : 'val2.1' } = props;

它甚至保持相对较好的可读性。

于 2020-06-29T13:40:35.567 回答
1

如果你使用 lodash,你可以使用_.cond

带 lodash/fp 的无点版本:

 const getWord = _.cond([
  [_.flow(_.get('distance'), _.eq(0)), _.constant('a')],
  [_.flow(_.get('distance'), _.eq(1)) && _.flow(_.get('difference'), _.gt(3)), _.constant('b')],
  [
    _.flow(_.get('distance'), _.eq(2))
    && _.flow(_.get('difference'), _.gt(5))
    && _.flow(_.get('key'), _.toString, _.gt(5)),
    _.constant('c'),
  ],
  [_.stubTrue, _.constant('d')],
]);
于 2021-01-18T09:56:42.623 回答
1

如果您想阅读一些不太可读的东西……这可能适合您。编写一个通用函数来获取一组条件(按照您编写 if/else 的顺序)和一组赋值。使用 .indexOf() 查找条件中的第一个事实,并返回该索引处的赋值数组值。顺序很关键,条件需要通过索引与您想要的分配匹配:

const conditionalAssignment = (conditions, assignmentValues) => assignmentValues[conditions.indexOf(true)];

您可以修改以处理truthy而不是struct true,如果indexOf为-1,请注意未定义的返回

于 2021-02-02T20:25:37.377 回答
0

我最近也遇到了这个问题,谷歌搜索把我带到了这里,我想分享一些我最近发现的关于这个的东西:

a && b || c

几乎是一样的

a ? b : c

只要b是真实的。如果b不是真的,你可以通过使用来解决它

!a && c || b

如果c是真的。

第一个表达式被评估为(a && b) || cas&&比 具有更高的优先级||

if ais truthy thena && b将评估为bif bis truthy,因此表达式变为b || cwhich 评估为bif it is truthy,就像a ? b : cwould if ais truthy 一样,如果a不是truthy,则表达式将c根据需要评估。

&&在and||技巧和?and语句的层之间交替||使用非嵌套三元 eslint 规则,它非常简洁(尽管我不建议这样做,除非没有其他出路)。

快速演示:

true ? false ? true : true ? false : true ? true ? true : false : true : true
// which is interpreted as
true ? (false ? true : (true ? false : (true ? (true ? true : false) : true))) : true
// now with the trick in alternate levels
true ? (false && true || (true ? false : (true && (true ? true : false) || true))) : true
// all of these evaluate to false btw

实际上,我通过选择一个b总是真实的例子来欺骗了一点,但是如果你只是设置字符串,那么这应该可以正常工作,因为'0'具有讽刺意味的是真实的。

于 2017-05-31T04:27:23.607 回答
0

对于这些情况,我一直在使用 switch(true) 语句。在我看来,这种语法感觉比嵌套的 if/else 运算符稍微优雅一些

switch (true) {
  case condition === true :
    //do it
    break;
  case otherCondition === true && soOn < 100 :
    // do that
    break;
}
于 2017-11-20T15:32:31.193 回答
0

我个人喜欢对一个衬里使用三元表达式。虽然,我不得不同意嵌套三元表达式会导致粗略的代码。

我最近开始使用Object构造函数来编写更清晰的代码:

let param: "one" | "two" | "three";

// Before
let before: number = param === "one" ? 1 : param === "two" ? 2 : 3;

// After
let after: number = Object({
    one: 1,
    two: 2,
    three: 3
})[param];

现实生活中的例子:

const opacity =
    Platform.OS === "android"
      ? 1
      : Object({
          disabled: 0.3,
          pressed: 0.7,
          default: 1,
        })[(disabled && "disabled") || (pressed && "pressed") || "default"];
于 2021-02-10T22:40:33.397 回答
0

ES6 对此打开了大门,这是对 switch 语句的不同看法。

Object.entries({
  ['a']: res.distance === 0,
  ['b']: res.distance === 1 && res.difference > 3,
  ['c']: (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c'
}).filter(n => n[1] === true)[0][0]
于 2021-07-20T17:09:41.687 回答