javascript - 运算符mdn - 逻辑运算符




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

JavaScript中的某些运算符执行隐式类型转换,有时用于类型转换。

一元! 运算符将其操作数转换为布尔值并取消它。

这个事实导致您可以在源代码中看到以下习语:

!!x // Same as Boolean(x). Note double exclamation mark

我看到一些代码似乎使用了一个我不认识的运算符,以两个感叹号的形式出现,如下所示: !! 。 有人可以告诉我这个运营商做了什么?

我看到这个的背景是,

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

! 是“boolean not”,它基本上将“enable”的值强制转换为其布尔相反的值。 第二 ! 翻转此值。 因此, !!enable表示“不能启用”,为您提供enable作为布尔值的值。


!! 将其右侧的值转换为其等效的布尔值。 (想想穷人的“打字”方式)。 它的目的通常是向读者传达代码并不关心变量中的值是什么,而是它的“真值”值


!! 构造是将任何JavaScript表达式转换为其布尔等效项的简单方法。

例如: !!"he shot me down" === true!!0 === false


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

          !!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!

!!foo应用unary not运算符两次并用于转换为布尔类型,类似于使用unary plus +foo转换为数字并连接空字符串''+foo以转换为字符串。

您也可以使用与基元类型相对应的构造函数( 使用new )来显式地转换值,而不是这些hacks,即

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

ifwhile语句和? 运算符使用真值来确定要运行的代码分支。 例如,零和NaN数字以及空字符串为false,但其他数字和字符串为true。 对象为true,但未定义的值和null都为false。

双重否定运算符!! 计算值的真值。 它实际上是两个运算符,其中!!x表示!(!x) ,其行为如下:

  • 如果x是假值,则!xtrue ,并且!!xfalse
  • 如果x是真值,则!xfalse ,并且!!xtrue

当在布尔上下文的顶层使用时( ifwhile? ), !! 运营商在行为上是无操作的。 例如, if (x)if (!!x)表示相同的事情。

实际用途

然而,它有几个实际用途。

一种用法是将对象有损压缩到其真值,这样您的代码就不会持有对大对象的引用并使其保持活动状态。 将!!some_big_object分配给变量而不是some_big_object ,可以将其用于垃圾收集器。 这对于生成对象或false值(例如null或未定义值(例如浏览器功能检测)的情况非常有用。

另一个用途,我在答案中提到了C的相应!! 操作员 ,使用“lint”工具查找常见的拼写错误和打印诊断信息。 例如,在C和JavaScript中,布尔运算的一些常见拼写错误会产生其输出不完全为布尔值的其他行为:

  • if (a = b)是赋值,则使用b的真值; if (a == b)是相等比较。
  • if (a & b)是按位AND; if (a && b)是逻辑AND。 2 & 50 (假值); 2 && 5是真的。

!! 操作员向lint工具保证,您所写的内容就是您的意思:执行此操作,然后获取结果的真值。

第三种用途是产生逻辑XOR和逻辑XNOR。 在C和JavaScript中, a && b执行逻辑AND(如果双方都为真,则为true), a & b执行按位AND。 a || b a || b执行逻辑OR(如果至少有一个为真,则为true)和a | b a | b执行按位OR。 有一个按位XOR(异或)作为a ^ b ,但没有内置运算符用于逻辑XOR(如果恰好一侧为真,则为true)。 例如,您可能希望允许用户在两​​个字段中的一个字段中输入文本。 你可以做的是将每个转换为真值并比较它们: !!x !== !!y


煮一些茶:

!! 不是运营商。 它是两用的! - 这是逻辑“非”运算符。

理论上:

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

  • 事实是false不是true (这就是为什么!false结果是true

  • 事实是, true并非false (这就是原因!true结果是false

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

  • 事实是, true 并非 true (这就是为什么!!true结果是true

  • 事实是false 并非 false (这就是为什么!!false结果为false

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

在实践中:

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


双布尔否定。 通常用于检查值是否未定义。


在看到所有这些很棒的答案后,我想补充一点使用原因 !! 。Currenty我正在使用Angular 2-4(TypeScript),我希望返回一个布尔值,就像false我的用户未经过身份验证一样。如果他未经过身份验证,则令牌字符串将为null""。我可以通过使用下一个代码块来完成此操作:

public isAuthenticated(): boolean {
   return !!this.getToken();
}

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

true === !!10

false === !!0

它将后缀转换为布尔值。


将oObject oObject为布尔值。 如果它是假的(例如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 null  

但如果你⇒

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

我只想补充一点

if(variableThing){
  // do something
}

是相同的

if(!!variableThing){
  // do something
}

但是当某些事情未定义时,这可能是一个问题。

// a is undefined, b is empty object.
var a, b = {};

// Both of these give error a.foo is not defined etc.
// you'd see the same behavior for !!a.foo and !!b.foo.bar

a.foo 
b.foo.bar

// This works -- these return undefined

a && a.foo
b.foo && b.foo.bar
b && b.foo && b.foo.bar

这里的技巧是&&s 链将返回它找到的第一个falsey值 - 这可以被提供给if语句等。所以如果b.foo未定义,它将返回undefined并跳过该b.foo.bar语句,我们得不到错误。

上面的返回未定义,但如果你有一个空字符串,false,null,0,undefined这些值将返回,并且很快我们在链中遇到它们 - []并且{}都是真实的。


我认为值得一提的是,结合逻辑AND / OR的条件不会返回布尔值,但是在&&和第一次成功或最后一次失败的情况下,在||的情况下,最后成功或首次失败 条件链。

res = (1 && 2); // res is 2
res = (true && alert) // res is function alert()
res = ('foo' || alert) // res is 'foo'

为了将条件转换为真正的布尔文字,我们可以使用双重否定:

res = !!(1 && 2); // res is true
res = !!(true && alert) // res is true
res = !!('foo' || alert) // res is true

看来!! 运算符导致双重否定。

var foo = "Hello World!";

!foo // Result: false
!!foo // Result: true

这么多答案做了一半的工作。 是的, !!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)

请注意,在将布尔值与数字进行比较时,会出现一些非直观的情况 ,其中布尔值将强制转换为数字( true1false0 )。 在这种情况下, !! 可能在精神上有用。 虽然,再次, 这些是您将非布尔值与硬类型布尔值进行比较的情况,即imo,这是一个严重的错误。 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 jive ,它会在消息框中输出-1! 在cmd.exe提示符下尝试,看看! 但是WScript.echo(-1 == test())仍然给你0,或者WScript是false把目光移开 这很可怕。

比较真实:)

但是,如果我有两个值,我需要检查相同的truthi / falsi-ness?

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

  • myVar1 === myVar20 === undefined ,显然是假的。
  • !!myVar1 === !!myVar2!!0 === !!undefined并且是真的! 同样诚实! (在这种情况下,两者都“有真实性”。)

因此,你真正需要使用“boolean-cast变量”的唯一地方是,如果你有一个情况,你要检查两个变量是否具有相同的真实性,对吧? 就是!! 如果你需要看看两个变量是真实的还是假的 (或者不是),那就是相同 (或不真实 )。

我无法想到一个伟大的,非人为的用例。 也许你在表单中有“链接”字段?

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日:

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

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

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

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

...当你没有消息时,你可能会惊讶地看到React呈现0 。 您必须为JSX显式返回false才能呈现。 上面的语句返回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}

请记住, 是一个JSX约定 ,而不是JavaScript固有的约定

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


这是一段来自角js的代码

var requestAnimationFrame = $window.requestAnimationFrame ||
                                $window.webkitRequestAnimationFrame ||
                                $window.mozRequestAnimationFrame;

 var rafSupported = !!requestAnimationFrame;

他们的目的是根据requestAnimationFrame中函数的可用性将rafSupported设置为true或false

通过以下方式检查可以实现:

if(typeof  requestAnimationFrame === 'function')
rafSupported =true;
else
rafSupported =false;

简短的方法可以使用!!

rafSupported = !!requestAnimationFrame ;

所以如果requestAnimationFrame被分配了一个函数,那么!requestAnimationFrame将是false而且还有一个! 这是真的

如果requestAnimationFrame被uninged undefined那么!requestAnimationFrame将是真的,还有一个! 这将是错误的


这是进行类型转换的一种非常模糊的方式。

! 不是 。 所以!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);

这里有很多很好的答案,但是如果你读到这么远,这有助于我“得到它”。 在Chrome(等)上打开控制台,然后输入:

!(!(1))
!(!(0))
!(!('truthy')) 
!(!(null))
!(!(''))
!(!(undefined))
!(!(new Object())
!(!({}))
woo = 'hoo'
!(!(woo))
...etc, etc, until the light goes on ;)

当然,这些都只是键入!! someThing,但添加的括号可能有助于使其更容易理解。







operators