为什么在if语句中要使用两个感叹号?

17

我最近读到一些代码使用!!将变量转换为布尔值,以便在if语句中进行评估。对我来说,这似乎有些多余,因为变量将被评估其布尔值。这样做是否有性能优势,还是为了更好的浏览器支持?

示例代码:

var x = 0;
var atTop = x===window.scrollY;
if(!!atTop){
   alert("At the top of the page.");
}

编辑:

我也见过这种情况,即操作数不是布尔类型的情况,但我一直认为在Javascript中使用if会将变量的布尔值作为条件进行评估,因为Javascript中的所有值都是“真值”或“假值”。

示例代码:

var x = 1;//or any other value including null, undefined, some string, etc
if(!!x){//x is "truthy"
   //wouldn't using if(x) be the same???
   console.log("something...");
}

可能是[JavaScript中的!!(not not)运算符是什么?]的重复问题。(https://dev59.com/jXRA5IYBdhLWcg3w6SRH) - takendarkk
4
@csmckelvey:这不是那个问题的重复。这个问题是:如果操作数已经是布尔值,是否有使用 !! 的理由? - Michael Liu
@csmckelvey 我知道 !! 的作用(将变量转换为布尔值),但我想知道在 if 语句中使用它是否有任何理由。 - Zamboni
这样做有性能上的好处吗,还是为了更好的浏览器支持?都不是。基本上这是一个巧妙的技巧,将 falsy / truthy 值转换为布尔值。在您的特定示例中,没有必要使用它,因为 atTop 已经是一个布尔值。 - kidA
1
在 if 语句中,这并不重要。但是当你准备在 Node 中发送 JSON 到浏览器或者反过来时,这会非常有帮助。 - rmn
显示剩余2条评论
4个回答

12

简短回答: 没有理由。

在您的代码中,它已经是一个boolean类型,没有必要转换,再转换回来:您总是会得到相同的结果。实际上,如果您有一个布尔值(truefalse),当您使用!!与任何一个值时,它将被转换回其原始值:

console.log(!!true); // Will always be "true"
console.log(typeof !!true); // It's still a "boolean" type
console.log(!!false); // Will always be "false"
console.log(typeof !!false); // It stills a "boolean" type

编辑后问题的答案: 是的, 它们是相同的。这就是 if(...)实际上正在尝试将任何类型转换为boolean的原因。

这里有一个小测试,您可以在initialArr数组中添加任何内容以测试它是否与if!!表现相同:

const initialArr = [
  undefined,
  null,
  true,
  false,
  0,
  3,
  -1,
  +Infinity,
  -Infinity,
  Infinity,
  'any',
  '',
  function() { return 1 },
  {},
  { prop: 1 },
  [],
  [0],
  [0, 1]
];

function testIsTheSame(arr) {
  let equolityCounter = 0;
  
  arr.forEach(item => {
    let ifStatement = false;
    let doubleNotStatement = !!item;
    
    if (item) {
      ifStatement = true;
    }

    if (
      ifStatement === doubleNotStatement &&
      typeof ifStatement === typeof doubleNotStatement
    ) {
      equolityCounter++;
    }
  });
  
  console.log(`Is the same: ${equolityCounter === arr.length}`);
}

testIsTheSame(initialArr);


2
非常感谢您的解释。 - Zamboni

5

在 if 语句中这样做并没有任何运行时的优势(或者客观上的优势)。if 语句执行时所得到的结果与双重否定所得出来的结果是完全相同的。它们没有区别。

双重否定是一种有用的表达式,可以基于真实性确保布尔值。例如考虑以下情况:

var text = undefined;
console.log(text); // undefined
console.log(!!text); // false

// It would make sense here, because a boolean value is expected.
var isTextDefined = !!text;

// It would also affect serialization
JSON.stringify(text); // undefined (non-string)
JSON.stringify(!!text); // "false"

我认为这是一个由于上述用例而养成的习惯,因此在希望引用变量的真实性时总是转换为布尔值。当规则始终适用时,对我们(人类和程序员)来说更容易遵循。你要么一直使用车里的方向指示器,即使周围没有人,要么很可能会偶尔(或者经常)忘记应该打信号。


3
我认为这主要是为了代码可读性而做的。我怀疑这不会有任何显著的性能或兼容性影响(请随意测试)。但从代码可读性方面来看,这意味着这个变量以前不是布尔值,但我们希望将其作为布尔值进行评估,如果您添加了关于这个变量的新逻辑,请记住这一点。虽然在你的情况下它已经是布尔值,所以它是100%多余的,但我猜只是某人习惯于过度应用上述原因,或者在其他地方看到并复制该模式而没有完全理解它(小故事时间:在 C# 中,您可以通过添加“@”来为受保护的术语命名变量名,因此var @class = "hello";,一个初级开发人员仅假设所有变量名都需要在前面加上@,并以这种方式编码。我想知道是否有人认为if没有比较运算符就需要在前面加上!!作为比较运算符)。

1
这完全取决于atTop变量的值。请参见此处的讨论。
简而言之,如果atTop的值与boolean不同,第一个否定将把它转换为一个布尔值(被否定)。第二个否定会将布尔值返回到原始atTop值的等效布尔值。
例如,如果atTopundefined,第一个!将把它变成boolean true,然后第二个!将把它变成boolean false(这将是undefined的等效布尔值)。
然而,在您的代码中,atTop的值是严格相等===的结果,这意味着您总是得到一个布尔值作为atTop的结果。因此,您不需要将非布尔值转换为布尔值,所以!!是不需要的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接