是什么 !! (不是)JavaScript 中的运算符?

我看到了一些似乎使用不认识的运算符的代码,它以两个感叹号的形式出现,像这样: !! 。有人可以告诉我这个操作员做什么吗?

我看到的背景是

this.vertical = vertical !== undefined ? !!vertical : this.vertical;

答案

Object转换为boolean 。如果它为假(例如 0, nullundefined等),则为false ,否则为true

!oObject  //Inverted boolean
!!oObject //Non inverted boolean so true boolean representation

所以!!不是运算符,而只是!操作员两次。

真实示例 “测试 IE 版本”:

let isIE8 = false;  
isIE8 = !! navigator.userAgent.match(/MSIE 8.0/);  
console.log(isIE8); // returns true or false

如果你⇒

console.log(navigator.userAgent.match(/MSIE 8.0/));  
// returns either an Array or null

但是如果你⇒

console.log(!!navigator.userAgent.match(/MSIE 8.0/));  
// returns either true or false

这是进行类型转换的令人费解的方法。

! 不是 。所以!truefalse!falsetrue!0true!1false

因此,您要将一个值转换为布尔值,然后将其求反,然后再次将其求反。

// Maximum Obscurity:
val.enabled = !!userId;

// Partial Obscurity:
val.enabled = (userId != 0) ? true : false;

// And finally, much easier to understand:
val.enabled = (userId != 0);

!!expr根据表达式的真实性返回布尔值( truefalse )。在非布尔类型上使用时更有意义。考虑以下示例,尤其是第三个示例及以后的示例:

!!false === false
           !!true === true

              !!0 === false
!!parseInt("foo") === false // NaN is falsy
              !!1 === true
             !!-1 === true  // -1 is truthy

             !!"" === false // empty string is falsy
          !!"foo" === true  // non-empty string is truthy
        !!"false" === true  // ...even if it contains a falsy value

     !!window.foo === false // undefined is falsy
           !!null === false // null is falsy

             !!{} === true  // an (empty) object is truthy
             !![] === true  // an (empty) array is truthy; PHP programmers beware!

冲泡一些茶:

!!不是运算符。这是双重用途! - 逻辑上的 “非” 运算符。


理论上:

!确定什么不是值的 “真相”:

  • 事实是false不是true (这就是!false导致true的原因)

  • 事实是true并非false (这就是!true导致false的原因)


!!确定不是值的 “真相”:

  • 事实是true 并非 true (这就是!!true导致true的原因)

  • 事实是false并非不是 false (这就是!!false导致false的原因)


我们希望在比较中确定的是关于参考值的 “真相”,而不是参考本身的 。在一个用例中,即使我们期望值是false (或 falsey),或者期望值不是 typeof boolean ,我们也可能想知道关于值的真相。


在实践中:

考虑一个简洁的功能,该功能通过动态键入 (也称为 “鸭子键入”)来检测功能(在这种情况下为平台兼容性)。如果用户的浏览器支持 HTML5 <audio>元素,我们想编写一个返回true的函数,但是如果<audio>未定义,我们不希望该函数抛出错误。而且我们不想使用try ... catch来处理任何可能的错误(因为它们很严重); 而且我们也不想在函数内部使用不会始终揭示该功能真相的检查(例如, document.createElement('audio')仍会创建一个名为<audio>的元素,即使 HTML5 <audio> )。


这是三种方法:

// this won't tell us anything about HTML5 `<audio>` as a feature
var foo = function(tag, atr) { return document.createElement(tag)[atr]; }

// this won't return true if the feature is detected (although it works just fine)
var bar = function(tag, atr) { return !document.createElement(tag)[atr]; }

// this is the concise, feature-detecting solution we want
var baz = function(tag, atr) { return !!document.createElement(tag)[atr]; }

foo('audio', 'preload'); // returns "auto"
bar('audio', 'preload'); // returns false
baz('audio', 'preload'); // returns true

每个函数都接受<tag>的参数和要查找的attribute ,但是它们各自根据比较确定的结果返回不同的值。

但是,等等,还有更多!

你们中的某些人可能注意到,在此特定示例中,可以使用性能稍强的一种方法来检查相关对象是否具有属性,从而简单地检查属性。有两种方法可以做到这一点:

// the native `hasOwnProperty` method
var qux = function(tag, atr) { return document.createElement(tag).hasOwnProperty(atr); }

// the `in` operator
var quux = function(tag, atr) { return atr in document.createElement(tag); }

qux('audio', 'preload');  // returns true
quux('audio', 'preload'); // returns true

我们离题了...

不管这些情况多么罕见,在某些情况下,最简洁,最有效,因此最可取的从非布尔值,可能未定义的值中获取true值的方法确实是使用!! 。希望这很荒谬。

!!将其右侧的值转换为其等效的布尔值。 (想想穷人的 “类型转换” 方式)。它的目的是通常传达给读者的是,代码不关心什么值是可变的,但它的“真” 值是。

!!foo两次应用一元非运算符,并用于转换为布尔类型,类似于使用一元加+foo转换为数字并连接空字符串''+foo转换为字符串。

除了这些技巧外,您还可以使用与原始类型相对应的构造函数( 使用new )来显式转换值,即

Boolean(foo) === !!foo
Number(foo)  === +foo
String(foo)  === ''+foo

这么多的答案完成了一半的工作。是的, !!X可以理解为 “X(以布尔值表示)的真实性”。但是!!实际上,对于弄清单个变量是真实的还是虚假的,并没有那么重要。 !!myVar === truemyVar相同。将!!X与 “真实” 布尔值进行比较并不是真正有用。

你从中获得什么!!是一种以可重复,标准化(和 JSLint 友好)方式相互检查多个变量的真实性的功能。

简单地铸造:(

那是...

  • 0 === falsefalse
  • !!0 === falsetrue

以上不是那么有用。 if (!0)给出的结果与if (!!0 === false) 。我想不出将变量转换为布尔值然后与 “true” 布尔值进行比较的好例子。

请从JSLint 的指示中查看 “ == 和!=“(注意:Crockford 会将其站点稍微移动一下;该链接可能会在某个时间点消失),其中有一些原因:

== 和!= 运算符会在比较之前键入强制。这很糟糕,因为它导致 '\ t \ r \ n'== 0 为真。这可以掩盖类型错误。 JSLint 无法可靠地确定 == 是否正确使用,因此最好不要使用 == 和!=,而始终使用更可靠的 === 和!== 运算符。

如果只在乎某个值是真实的还是虚假的,则使用缩写形式。代替
(foo != 0)

说啊
(foo)

而不是
(foo == 0)


(!foo)

请注意,在某些不直观的情况下 ,将布尔值与数字进行比较时,布尔值将被强制转换为数字(将true强制转换为1 ,将false0 )。在这种情况下, !!可能在心理上有用。同样,在这些情况下,您正在将非布尔值与硬类型的布尔值进行比较,这是一个严重的错误。 if (-1)仍然是这里的路。

╔═══════════════════════════════════════╦═══════════════════╦═══════════╗
║               Original                ║    Equivalent     ║  Result   ║
╠═══════════════════════════════════════╬═══════════════════╬═══════════╣
║ if (-1 == true) console.log("spam")   ║ if (-1 == 1)      ║ undefined ║
║ if (-1 == false) console.log("spam")  ║ if (-1 == 0)      ║ undefined ║
║   Order doesn't matter...             ║                   ║           ║
║ if (true == -1) console.log("spam")   ║ if (1 == -1)      ║ undefined ║
╠═══════════════════════════════════════╬═══════════════════╬═══════════╣
║ if (!!-1 == true) console.log("spam") ║ if (true == true) ║ spam      ║ better
╠═══════════════════════════════════════╬═══════════════════╬═══════════╣
║ if (-1) console.log("spam")           ║ if (truthy)       ║ spam      ║ still best
╚═══════════════════════════════════════╩═══════════════════╩═══════════╝

而且取决于您的引擎,事情变得更加疯狂。以 WScript 为例。

function test()
{
    return (1 === 1);
}
WScript.echo(test());

由于Windows某些历史记录 ,将在消息框中输出 - 1!在 cmd.exe 提示中尝试一下,看看!但是WScript.echo(-1 == test())仍然为您提供 0,或者 WScript 为false别看太可怕了

比较真实性:)

但是,如果我有两个值需要检查真实性 / 虚假性,该怎么办?

假设我们的myVar1 = 0;myVar2 = undefined;

  • myVar1 === myVar20 === undefined ,显然为 false。
  • !!myVar1 === !!myVar2!!0 === !!undefined ,是真的!同样的真实性! (在这种情况下,两个 “都具有虚假的真实性”。)

因此,您真正需要使用 “布尔值转换的变量” 的唯一地方是,当您要检查两个变量是否具有相同的真实性时,对吗?也就是说, 使用!!如果您需要查看两个变量是否都是真实的或都是虚假的 (或不是),那就是相等 (或不是) 真实性

我无法想到一个出色的,非人为的用例。也许您具有表单中的 “链接” 字段?

if (!!customerInput.spouseName !== !!customerInput.spouseAge ) {
    errorObjects.spouse = "Please either enter a valid name AND age " 
        + "for your spouse or leave all spouse fields blank.";
}

因此,现在,如果您对配偶双方的姓名和年龄均不真实, 或者对配偶姓名和年龄均不真实,则可以继续。否则,您只有一个带有值的字段(或者很早就安排了婚姻),并且需要在errorObjects集合上创建一个额外的错误。


编辑 2017 年 10 月 24 日,19 年 2 月 6 日:

期望显式布尔值的第三方库

这是一个有趣的案例!!当第三方库期望显式布尔值时,此方法可能很有用。

例如, JSX(React)中的 False 具有特殊含义 ,不会因简单的虚假行为而触发。如果您尝试在 JSX 中返回类似以下内容的内容,则期望messageCount的 int ...

{messageCount && <div>You have messages!</div>}

... 当您收到零条消息时,看到 React 将0渲染可能会让您感到惊讶。您必须显式返回 false 才能使 JSX 无法呈现。上面的语句返回0 ,这是 JSX 乐意呈现的。它不能告诉您没有Count: {messageCount && <div>Get your count to zero!</div>} (或其他较不人为的东西)。

  • 一种解决方法涉及 bangbang,它将0强制为!!0 ,这是false
    {!!messageCount && <div>You have messages!</div>}

  • JSX 的文档建议您更加明确,编写自我注释的代码,并使用比较来强制使用布尔值。
    {messageCount > 0 && <div>You have messages!</div>}

  • 我更愿意用三元来处理虚假行为 -
    {messageCount ? <div>You have messages!</div> : false}

在 Typescript 中也是如此:如果您有一个返回布尔值的函数(或者您正在为布尔变量赋值),则 [通常] 您无法返回 / 分配布尔 y 值;它必须是一个强类型的布尔值。这意味着, 如果myObject是强类型的 ,则return !myObject;适用于返回布尔值但return myObject;的函数return myObject;没有。您必须return !!myObject以符合 Typescript 的期望。

Typescript 例外?如果myObjectany ,那么您将返回 JavaScript 的 Wild West,并且可以不带!!返回它!! ,即使您的返回类型是布尔值。

请记住, 这些是 JSX 和 Typescript 约定 ,而不是 JavaScript 固有的约定

但是,如果在渲染的 JSX 中看到奇怪的0 ,请考虑宽松的虚假管理。

它只是逻辑非运算符,两次 - 用于将某些内容转换为布尔值,例如:

true === !!10

false === !!0

它将后缀转换为布尔值。

这是一个双not操作。第一!将值转换为布尔值并反转其逻辑值。第二!反转逻辑值。