我们都知道「!」是「非」的意思,那么「!!」就是「双非」的意思,「双非」是啥,说的就是写代码的人毕业的学校既不是 985 又不是 211,懂了吗。
众所周知,JavaScript 是非常坑的语言。正所谓动态类型一时爽,代码重构火葬场。所以 JavaScript 的特性之一,就是质问你的灵魂,为什么要从事 JavaScript 编程。而这个 !!,就是最强有力的质问。即表达了对编程者动机的质问,也是 JavaScript 情感的宣泄。表达了作者内心焦躁不安却又无处安放的思想感情。
JavaScript 与你【 !亲!故】,又何你让你【 想入!!】步入 JS 编程?你不知这 JS【 !黑!白】,平日处处【 是是!!】吗?就不怕这 JS 相处多了,使得你【 !人!鬼】,【 !牛!马】吗!!
JavaScript 中有 真值(truthy) 和 虚值(falsey) 的概念。
变量有不同的值和类型,truthy 的值不等同于 true。但在布尔值上下文中,truthy 的值会被强制转化为 true,同理,虚值(或者假值)会被转化为 false。
那么啥是 布尔值上下文(Boolean context)?简单来说就是类似
if (something) { // something 就处在布尔值上下文中,会被强制转化为 true 或者 false
// do something.
}
那么哪些是真值,哪些是假值呢?
真值 truthy | 虚值 (假值)falsey |
---|---|
true | false |
{} | null |
[] | undefined |
42 | 0 |
-42 | 0n |
"string" | "" |
new Date() | `` |
Infinity | document.all |
-Infinity | NaN |
new RegExp("abc") |
那么 【!!】这个双非运算符,可以把真值转化为 true,虚值转化成 false。
好机灵抖完了,认真说下为什么会有这种搞法,就算我写了快十年 JavaScript 我还是要说一句,它是一个操蛋的语言,其中一个最为操蛋之处就是「隐式类型转换」,它有多坑,我都懒得说,在 review 新人代码的时候,这些年来我可能一半的掉发都是因为它,以至于我最后为了自己的头发能够多活几年找女朋友,就在团队的 ESLint 中禁用了几乎所有的「隐式类型转换」。
在 JS 中不止是 「!」,而是几乎所有的运算符在对两个不同类型的值进行操作的时候,都会触发「隐式类型转换」的机制。
题目问的是「!!」那么,就在这里重点说下「!」运算符,它将其他类型的值转为布尔值。
隐式类型转换的规律之整数转布尔值:它会把 0 转换为 false,把不是 0 的数字转换为 true。
var a = 5;
var b = !a; // 5 不是整数吗,居然能取 !,但是在 JS 中整数就是可以取 !。
// 上面步骤中的 ! 操作符,让 JS 运行环境先将 5 「隐式类型转换」 成了 true 然后取 !
console.log(b); // => false,于是它就成了布尔值 false
// 这里的 ! 操作符将 5 变成了 true 然后取了两次 !
// 就相当于直接得到 JS 运行环境将 5 直接「隐式类型转换」成布尔值的结果了
var c = !!a;
console.log(c); // => true,你看我们成功的吧整数 5 变成了一个布尔值,只不过这回它的值为 true
// 小练习,下面的 e 和 f 还有 !!g 分别是什么值
var d = 0;
var e = !d;
console.log(e);
var f = !!d;
console.log(f);
var g = -5;
console.log(!!g);
隐式类型转换的规律之字符串转布尔值:它会把空字符串转为 false 非空字符串转为 true。
var a = '非空字符串';
var b = !a; // '非空字符串'不是字符吗,居然能取 !,但是在 JS 中就是可以取 !。
// 上面步骤中的 ! 操作符,让 JS 运行环境先将'非空字符串'「隐式类型转换」 成了 true 然后取 !
console.log(b); // => false,于是它就成了布尔值 false
// 这里的 ! 操作符将'非空字符串'变成了 true 然后取了两次 !
// 就相当于直接得到 JS 运行环境将'非空字符串'直接「隐式类型转换」成布尔值的结果了
var c = !!a;
console.log(c); // => true,你看我们成功的吧整数'非空字符串'变成了一个布尔值,只不过这回它的值为 true
// 小练习,下面的 e 和 f 分别是什么值
var d = '';
var e = !d;
console.log(e);
var f = !!d;
console.log(f);
隐式类型转换的规律之离散结构转布尔值:它会把离散结构(数组,对象,函数,集合等,引用类型的变量)统一转化为 true 无论它们中有没有成员。
var a = [1,2,3];
console.log(!!a); // => true 数组都能取 !!
var b = {name: 'weijie'};
console.log(!!b); // => true 字典也可以
var c = new Set([]); // 空集
console.log(!!c); // => true 集合也行, 就算是空的也是 true
var d = function(x = 0) { return x};
console.log(!!d); // => true 函数也可以哦
上面举的是一些常见的(可能也不太常见)的三个例子,你应该可以看明白了「!!」就是直接得到 JS 运行环境将表达式执行「隐式类型转换」为布尔值的结果。
但是,关于隐式类型转换的情况多如牛毛,很多时候是不讲道理的,如下
var a = {};
var b = function() {};
// 上面的只是说的 ! 运算符的转换,下面还有其他的转换
console.log(a+b); // 猜猜是啥
console.log(a<b); // 猜,再猜
console.log(a>b); // 来继续继续继续
console.log(!!a > 0); // 猜猜猜来
console.log(!b == 0); // 来来来来来
//.....
以上,你也看到了, JS 是能够这么瞎搞的,情况多如牛毛,没法一一举例,许多时候也没有太多规律,所以为了团队和谐和项目质量,尽量不要去使用「隐式类型转换」,包括「!!」。