如何编写三元运算符(aka if)表达式而不重复自己

use*_*934 102 javascript conditional-operator

例如,像这样:

var value = someArray.indexOf(3) !== -1 ? someArray.indexOf(3) : 0
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来写这个?同样,我不是在寻找上面确切问题的答案,只是一个例子,说明何时可能在三元运算符表达式中重复操作数...

sle*_*man 175

就个人而言,我发现最好的方法仍然是一个很好的if陈述:

var value = someArray.indexOf(3);
if (value === -1) {
  value = 0;
}
Run Code Online (Sandbox Code Playgroud)

  • @JackAidley:"OP中的三元组是一个更好的解决方案,因为它永远不会进入中间状态." - 我不得不反对.这比OP的代码更具可读性,完全是惯用的.它也使OP的逻辑中的错误对我来说更明显(即,如果indexOf()返回零会发生什么?如何区分"真实"零和"未找到"零?). (44认同)
  • 需要明确的是,这个答案主张使用变量来存储中间结果,而不是使用"if"语句来代替三元组.三元语句仍然经常用于作为表达式的一部分执行选择. (31认同)
  • 不正确.第一行之后存储在"value"中的值是中间值.它不包含正确的值,然后在下一行固定,因此是中间值.只有在`if`语句结束后,`value`才包含正确的值.OP中的三元组是一个更好的解决方案,因为它永远不会进入中间状态. (14认同)
  • @Triptych:再看一遍.它根本不使用中间变量.相反,它将结果直接分配给最终变量,然后在满足条件时覆盖它. (4认同)
  • 这接近我会做的,除了这里的`value`在技术上是变异的,我试图避免. (2认同)
  • @ Marc.2377它不是"真正的"逻辑错误,而是更多的"可能的"逻辑错误.如果`someArray.indexOf(3)`返回0,则`value`设置为0,就像返回-1一样.具有相同返回值的两个不同状态(特别是当一个是特殊情况时)*可能*是一个错误,但不一定. (2认同)

Dai*_*Dai 101

代码应该是可读的,所以简洁不应该意味着简洁无论成本 - 因为你应该重新发布到https://codegolf.stackexchange.com/ - 所以我建议使用第二个局部变量命名index以最大化阅读可理解性(运行成本最低,我注意到:):

var index = someArray.indexOf( 3 );
var value = index == -1 ? 0 : index;
Run Code Online (Sandbox Code Playgroud)

但是如果你真的想减少这种表达,因为你对你的同事或项目合作者来说是一个残酷的虐待狂,那么这里有4种方法你可以使用:

1:var声明中的临时变量

当用逗号分隔时,您可以使用var语句的能力来定义(和分配)第二个临时变量index:

var index = someArray.indexOf(3), value = index !== -1 ? index: 0;
Run Code Online (Sandbox Code Playgroud)

2:自动执行匿名功能

另一种选择是自执行匿名函数:

// Traditional syntax:
var value = function( x ) { return x !== -1 ? x : 0 }( someArray.indexOf(3) );

// ES6 syntax:
var value = ( x => x !== -1 ? x : 0 )( someArray.indexOf(3) );
Run Code Online (Sandbox Code Playgroud)

3:逗号操作员

JavaScript也支持臭名昭着的"逗号运算符",它也存在于C和C++中.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator

如果要在需要单个表达式的位置包含多个表达式,可以使用逗号运算符.

您可以使用它来引入副作用,在这种情况下,通过重新分配到value:

var value = ( value = someArray.indexOf(3), value !== -1 ? value : 0 );
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为var value首先解释(因为它是一个语句),然后是最左边的,最里面的value赋值,然后是逗号运算符的右边,然后是三元运算符 - 所有合法的JavaScript.

4:在子表达式中重新分配

评论员@IllusiveBrian指出,如果将赋值value用作带括号的子表达式,则不需要使用逗号运算符(在前面的示例中):

var value = ( ( value = someArray.indexOf(3) ) !== -1 ? value : 0 );
Run Code Online (Sandbox Code Playgroud)

请注意,在逻辑表达式中使用否定词可能更难以遵循 - 所以上述所有示例都可以通过更改idx !== -1 ? x : yidx == -1 ? y : x:

var value = ( ( value = someArray.indexOf(3) ) == -1 ? 0 : value );
Run Code Online (Sandbox Code Playgroud)

  • 所有这些都是那种"聪明"的编码,它会让我盯着它看几秒钟然后才想"嗯,我猜这有用".它们没有明确的帮助,因为代码的读取比写入更重要. (97认同)
  • @ g.rocket方法1是100%可读的和明确的,并且去,如果你想避免重复(这可能是真正有害的,如果你调用一些复杂的和有问题的功能,而不是一个简单的`indefOf`)的唯一途径 (18认同)
  • 对于#3,我相信你可以简化为`var value =((value = someArray.indexOf(3))=== -1?0:value);`而不是使用逗号. (6认同)
  • 同意,#1是非常易读和可维护的,另外两个并不是那么多. (5认同)
  • 在第二个示例_Self-execution anonymous function_中,您可以从箭头函数中省略括号.`var value =(x => x!== -1?x:0)(arr.indexOf(3));`因为只有一个参数. (2认同)

Cri*_*par 53

对于数字

你可以使用这个Math.max()功能.

var value = Math.max( someArray.indexOf('y'), 0 );
Run Code Online (Sandbox Code Playgroud)

它将保持结果的边界,0直到第一个结果大于0情况.如果结果indexOf-1,它将返回0大于-1.

对于布尔值和布尔值

对于JS,没有一般规则AFAIK特别是因为如何评估虚假值.

但是如果有什么东西可以帮助你,那么大部分时间都是或者运算符(||):

// Instead of
var variable = this_one === true ? this_one : or_this_one;
// you can use
var variable = this_one || or_this_one;
Run Code Online (Sandbox Code Playgroud)

你必须非常小心这一点,因为在你的第一个例子中,indexOf可以返回0,如果你评估0 || -1它会返回,-1因为它0是一个虚假值.

  • 谢谢.我想我的例子很糟糕我只是举一个例子哈哈没有寻求解决问题的确切问题.我遇到了一些scnarios像我想使用三元组的例子,但最终重复:( (2认同)

Tri*_*ych 26

不是,只是使用另一个变量.

你的例子概括为这样的东西.

var x = predicate(f()) ? f() : default;
Run Code Online (Sandbox Code Playgroud)

您正在测试计算值,然后在传递某个谓词时将该值分配给变量.避免重新计算计算值的方法很明显:使用变量来存储结果.

var computed = f();
var x = predicate(computed) ? computed : default;
Run Code Online (Sandbox Code Playgroud)

我理解你的意思 - 似乎应该有一些方法可以做到这一点,看起来更清洁.但我认为这是做到这一点的最好方式(惯用).如果由于某种原因你在代码中重复了这个模式,你可能会编写一个小帮助函数:

var setif = (value, predicate, default) => predicate(value) ? value : default;
var x = setif(someArray.indexOf(3), x => x !== -1, 0)
Run Code Online (Sandbox Code Playgroud)


Lyu*_*mir 17

编辑:现在是,现在用JavaScript 进行Nullary-coalescing的提议!


使用 ||

const result = a ? a : 'fallback value';

相当于

const result = a || 'fallback value';

如果转换aBoolean返回false,result则将被赋值'fallback value',否则为值a.


注意边缘情况下a === 0,其造型为falseresult会(错误地)走'fallback value'.使用这样的技巧需要您自担风险.


PS.诸如Swift之类的语言具有nil-coalescing operator(??),它具有类似的用途.例如,在Swift中你会写result = a ?? "fallback value"一些非常接近JavaScript的东西const result = a || 'fallback value';

  • 这仅在函数失败时返回falsey值时才有效,但`indexOf()`不能在此模式中使用. (5认同)
  • PHP(> 7.0)和C#也支持null合并运算符.语法糖但肯定可爱. (3认同)
  • 是*真的*如何`||`在JavaScript中有效吗?如果我正确地理解你,那么它的工作方式与许多其他主要语言不同(我主要考虑C及其后代[C++,Java等])即使这真的是`||`在JavaScript中的工作方式,我也是如此会建议不要使用这样的技巧,要求维护者知道关于语言的特殊怪癖.虽然这个技巧很酷,但我认为这是不好的做法. (2认同)

Dav*_*eau 8

使用提取变量重构:

var index = someArray.indexOf(3);
var value = index !== -1 ? index : 0
Run Code Online (Sandbox Code Playgroud)

const代替了更好var.你还可以做一个额外的提取:

const index = someArray.indexOf(3);
const condition = index !== -1;
const value = condition ? index : 0;
Run Code Online (Sandbox Code Playgroud)

在实践中,使用比更有意义的名称index,conditionvalue.

const threesIndex = someArray.indexOf(3);
const threeFound = threesIndex !== -1;
const threesIndexOrZero = threeFound ? threesIndex : 0;
Run Code Online (Sandbox Code Playgroud)


Iły*_*sov 6

我个人更喜欢两种变体:

  1. 像@slebetman建议的那样纯粹

  2. 单独的函数,用默认值替换无效值,如下例所示:

function maskNegative(v, def) {
  return v >= 0 ? v : def;
}

Array.prototype.indexOfOrDefault = function(v, def) {
  return maskNegative(this.indexOf(v), def);
}

var someArray = [1, 2];
console.log(someArray.indexOfOrDefault(2, 0)); // index is 1
console.log(someArray.indexOfOrDefault(3, 0)); // default 0 returned
console.log(someArray.indexOfOrDefault(3, 123)); // default 123 returned
Run Code Online (Sandbox Code Playgroud)


Tyz*_*oid 6

您可能正在寻找一个合并运营商.幸运的是,我们可以利用Array原型创建一个:

Array.prototype.coalesce = function() {
    for (var i = 0; i < this.length; i++) {
        if (this[i] != false && this[i] != null) return this[i];
    }
}

[null, false, 0, 5, 'test'].coalesce(); // returns 5
Run Code Online (Sandbox Code Playgroud)

通过向函数添加参数,可以进一步推广这种情况:

Array.prototype.coalesce = function(valid) {
    if (typeof valid !== 'function') {
        valid = function(a) {
            return a != false && a != null;
        }
    }

    for (var i = 0; i < this.length; i++) {
        if (valid(this[i])) return this[i];
    }
}

[null, false, 0, 5, 'test'].coalesce(); // still returns 5
[null, false, 0, 5, 'test'].coalesce(function(a){return a !== -1}); // returns null
[null, false, 0, 5, 'test'].coalesce(function(a){return a != null}); //returns false
Run Code Online (Sandbox Code Playgroud)


Ada*_*dam 5

我喜欢@ slebetman的回答.其中的评论表达了对变量处于"中间状态"的担忧.如果这是你的一个大问题,那么我建议将其封装在一个函数中:

function get_value(arr) {
   var value = arr.indexOf(3);
   if (value === -1) {
     value = 0;
   }
   return value;
}
Run Code Online (Sandbox Code Playgroud)

然后打电话

var value = get_value( someArray );
Run Code Online (Sandbox Code Playgroud)

如果你在其他地方使用它们,你可以做更多的通用功能,但如果它是一个非常具体的情况,不要过度工程.

但说实话,我会像@slebetman那样做,除非我需要从几个地方重新使用.