我绝对不是javascript专家,但我一直在阅读MarkPilger的"潜入HTML5"网页,他提到了一些我想更好理解的东西。
他说:
Finally, you use the double-negative trick to force the result to a Boolean value (true or false).
1 2 3
| function supports_canvas() {
return !!document.createElement('canvas').getContext;
} |
如果有人能更好地解释这一点,我会感激的!
逻辑非运算符!将值转换为与其逻辑值相反的布尔值。
第二个!将前一个布尔结果转换回其原始逻辑值的布尔表示。
对于逻辑非运算符,从这些文档中:
Returns false if its single operand can be converted to true; otherwise, returns true.
所以如果getContext给你一个"假"值,!!会使它返回布尔值false。否则将返回true。
"虚假"值为:
- false
- NaN
- undefined
- null
- ""(空字符串)
- 0
- @格林恩:用户113716没有列出所有。他忘记了0岁。我不是指一元减零,而是结果,它是一个单独的值。例如,可以通过将-0赋给变量来创建它。
- @marcodewit:它们在javascript中是相等的值。-0 === 0 // true用户113716没有遗漏与问题相关的任何内容。
- @斜视:首先,用户113716试图复制错误值的列表,但我仍然认为其中缺少-0。我认为这与问题有关,因为也许有人会认为,如果0是假的,那么-0会因为负而再次变为真。-0===0的计算结果仅为true,因为它是以这种方式定义的,而不是因为它是相同类型的相等值。另外,如果它们真的相等,那么1/0应该给出与1/-0相同的结果。
- @Marcodewit:用户可能会这么认为,但没有理由这么做。这个问题是关于逻辑运算的,而0总是错误的。他的回答就是这样说的。-0 === 0表明它们是同一类型的相等值,因为它是这样定义的。您的除法示例是一种非常特殊的情况,与这个问题或99.999%的0值的使用无关。嘿,即使是.toString()也以同样的方式代表它们。(-0).toString(); //"0"
- Object.is(0, -0)返回false,所以它们不是同一回事。
当把javascript放在一个需要布尔值的上下文中时,它对于被认为是"真"和"假"的内容有一套令人困惑的规则。但是逻辑非运算符!总是产生一个适当的布尔值(true和false常量之一)。通过链接其中的两个,习语!!expression产生了一个与原始表达式具有相同真实性的布尔值。
你为什么要麻烦?因为它使你所展示的功能更具可预测性。如果没有双负,它可能返回undefined、Function对象,或者与Function对象完全不同的对象。如果这个函数的调用者对返回值做了一些奇怪的事情,那么整个代码可能会出现错误行为("奇怪"这里的意思是"除了强制布尔上下文的操作之外的任何操作")。双重否定成语阻止了这一点。
- 这不是一套"混乱"的规则。
- @abody97列表(上面有帮助地显示)既不尽可能短(false;任何其他要求显式比较运算符),也不尽可能长(至少添加{}和[])。所以你必须记住列表而不是规则。这就是我所说的一个令人困惑的语言特性。
- 当然可以:这是一个列表,不是规则。我认为这是非常主观的,不管它是否令人困惑。我个人认为,当被投射到一个布尔值时,知道什么是"虚假的"和什么是"真实的"是非常直观的。
- 无论如何,我不是有意强烈反对。
在javascript中,使用"bang"运算符!!)如果给定值为真、1、非空等,则返回真。如果该值为未定义、空、0或空字符串,则返回假。
所以bang运算符总是返回一个布尔值,但它将表示与您开始使用的值相反的值。如果您得到该操作的结果并再次"砰"地一声,您可以再次反转它,但最终仍然得到一个布尔值(而不是未定义、空值等)。
使用bang两次将得到一个可能未定义、空值等的值,并使其变为普通的false。它需要一个可能是1的值,"真的",等等,并使它变得简单。
代码可以写在:
1 2 3 4
| var context = document.createElement('canvas').getContext;
var contextDoesNotExist = !context;
var contextExists = !contextDoesNotExist;
return contextExists; |
使用!!variable保证将类型转换为布尔型。
举个简单的例子:
1 2 3 4 5
| "" == false (is true)
"" === false (is false)
!!"" == false (is true)
!!"" === false (is true) |
但是,如果你在做如下的事情,使用它是没有意义的:
1 2 3
| var a =""; // or a = null; or a = undefined ...
if(!!a){
... |
if将其强制转换为布尔值,因此不需要进行隐式的双负强制转换。
!向boolean投射"某物"/"任何东西"。
!!返回原始布尔值(并保证表达式现在是布尔值,不管以前是什么)
第一个!将变量强制为布尔类型并将其反转。第二个!再次反转它(为您检查的内容提供原始(正确)布尔值)。
为了清楚起见,最好使用
- Boolean()创建了一个装箱布尔值,其行为与!!创建的原始布尔值不同(例如,typeof将报告"object")。因此,最好使用!!。
- 使其返回原始布尔值:return (new Boolean(...)).valueOf()。
- @扎克,这不适合我。它只在与new结合时才起作用。
- @亚历克斯,我可能已经过时了。这当然是我所说的……大约六年前,JS的口译员还不太成熟。此外,当前行为可能依赖于浏览器。
- 我知道这有点陈词滥调,但我认为指出砰砰的一声很重要。!)的性能远远高于布尔值(val)。jspef.com/bang-bang-vs-boolean
document.createElement('canvas').getContext可以对undefined或对象引用进行评估。!undefined生成true,![some_object]生成false。这几乎是我们需要的,只是颠倒过来。因此,!!用于将undefined转换为false和对象引用true。
这与javascript的弱类型有关。document.createElement('canvas').getContext是一个函数对象。通过预先准备单个!,它将其评估为布尔表达式,并翻转答案。通过预先准备另一个!,它会翻转答案。最终结果是,函数将其作为布尔表达式计算,但返回实际的布尔结果,而不是函数对象本身。预处理!!是将表达式类型转换为布尔类型的一种快速而肮脏的方法。
如果document.createElement('canvas').getContext不是undefined或null的话,它将返回true。否则返回false。