null >= 0,你觉得这个答案是什么?
null >= 0
的答案
最近公司晚上直播网课『重学前端』,发现有很多细碎的小知识都被遗忘在脑海的角落里了,比如说这篇博客想写的操作符。。。
事情是这样的,写需求遇到个问题,当 count
为 null
或者 undefined
的时候,界面展示...
,而当 count
为 0 或大于 0 的数字时,展示 count
,一个很简单的问题嘛,我的写法是 count || count === 0
,看起来一切正常,自信的去 review,然后发现似乎可以替换为一个更简洁的写法count >= 0
,然后自测过程中,发现界面上的...
不见了,排查了半天 bug,发现似乎是因为 count 为 null,而 null>=0 这段代码导致的。
抱着不可置信的态度,我在浏览器输入null >= 0
,回车!
😱😱😱 我当时是震惊的。。。小伙伴也是

然后不信邪的继续在浏览器敲出了如下试验。。。。满脑子问号。。。。
然后去网上搜了下,发现也有不少人问到了这个问题。看了下有人提到了,null >= 0
是因为,取了! null < 0
这种思路,想着应该不会有这么迷的设计原理,于是接着寻找答案,然后 stackoverflow 在上发现了这个提问,里面的讨论似乎解决了我一半的疑惑。
大致意思是,js 中 关系操作符 和 相等操作符 是两个概念,也就是背后的处理逻辑是完全不一样的。
- 关系操作符:
null == 0
当中,null
咩有被转换类型,所以得到了false
- 相等操作符:
null >= 0
当中,null
被转换为数字了,所以null
转换为 0,0 >= 0
为true
undefined >= 0
的答案
然后我新的疑惑又来了,印象里,undefined
是和 null
很类似的存在,那为什么 undefined >= 0
为 false 呢?这种问题网络上已经在网上搜不到了…
不过这么基础的知识,掏出红宝书总会找到答案的(比如我买的这版是第三章第 5 节, P50-51),里面详细介绍了关系操作符的运算逻辑。
那么对于 null
和 undefined
来说,因为右侧的 0
是 Number
类型的,那么左侧也会直接转换成 Number
类型。
- 【null】:
Number(null)
为 0,0 >= 0
结果为true
- 【undefined】:
Number(undefined)
为NAN
,NAN >= 0
结果为false
,(NAN
和谁做关系比较,结果都为false
)
操作符
既然说又重温了操作符的知识,那么就顺便再多写写别的操作符。
一元操作符
只能操作一个值的操作符。一元操作符是 ECMAScrip 里最简单的操作符。
递增和递减操作符
这种操作符借鉴于 C,分为前置型和后置型。
- 前置型:
1
2
3
4
5
6
7
8let age = 29;
++age;
// 等价于
let age = 29;
age = age + 1;
let currentAge = 20;
const anotherAge = --age + 2; //21- 后置型:
后置和前置有一个很重要的区别,即递增和递减操作是在包含他们的语句 被求值之后 才执行的。
1
2
3
4
5
6// 这个区别在某些情况下不是问题,例如:
let age = 29;
age++;
// 递增操作符放到语句后面并不会改变语句的结果 ,因为递增操作符是这条语句的唯一操作。但是当语句还包含其他操作时,上述的区别就很明显。
let currentAge = 20;
const anotherAge = age-- + 2; //22上述四种操作符(前置加减,后置加减)对任何值都适用,在应用不同的值时,递增和递减操作符遵循下列规则
- 应用一个包含有效数字字符的字符串时,先将其转换为数字,再执行,输出 Number 类型
- 应用一个不包含有效数字字符的字符串时,输出 Number 类型的 NAN
- 布尔值,先将其转换为数字,再执行,输出 Number 类型
- 浮点数,执行加减 1 的操作
- 应用对象时,先调用对象的 valueOf()取得一个可操作的值,然后对该值应用前述规则。如果结果是 NAN,则再调用 toString()方法后再应用前述规则。输出 Number 类型
一元加和减操作符
一元加操作符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15let num = 25;
num = +num; // 25
// 对于数值不会产生任何影响,但是对于非数值应用时,该操作符会像Number()转型函数一样对这个值执行转换
let s1 = "01";
s1 = +s1; // 1
let s2 = "1.1";
s2 = +s2; // 1.1
let s3 = "s3";
s3 = +s3; // NAN
let b = false;
b = +b; // 0
let f = 1.1;
f = +f; // 1.1
let o = { valueOf: () => -1 };
o = +o; // -1一元减操作符
一元减操作符主要用于表示负数,例如将 1 转为-1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15let num = 25;
num = -num; // -25
// 对于非数值应用时,该操作符会像Number()转型函数一样对这个值执行转换
let s1 = "01";
s1 = -s1; // -1
let s2 = "1.1";
s2 = -s2; // -1.1
let s3 = "s3";
s3 = -s3; // NAN
let b = false;
b = -b; // 0
let f = 1.1;
f = -f; // -1.1
let o = { valueOf: () => -1 };
o = -o; // 1
位操作符
- 按位非(NOT)
1 | let num1 = 25; //二进制 0000 0000 0000 0000 0000 0000 0001 1001 |
- 按位与(AND)
1 | let result = 25 & 3; //1 |
- 按位或(OR)
1 | let result = 25 | 3; //27 |
- 按位异或(XOR)
1 | let result = 25 ^ 3; //26 |
- 左移
1 | let num1 = 2; //二进制10 |
其实还有 无符号右移 和 有符号右移 两种操作符,但是因为目前看用到的场景很少,所以也不做描述了,感兴趣可以去翻阅资料(红宝书 3.5.2 章节)
布尔操作符
- 逻辑非
1 | let b1 = !false; //true |
- 逻辑与
1 | const result = true && false; //false |
逻辑与的真值表如下:
| 第一个操作数 | 第二个操作数 | 结果 |
| ———— | ———— | —– |
| true | true | true |
| true | false | false |
| false | true | false |
| false | false | false |
逻辑与可以应用于任何类型的操作数,而不仅是布尔值。在有一个操作数不是布尔值时,就不一定返回布尔值,此时,它遵循以下规则:
- 如果第一个是对象,则返回第二个操作数
- 如果第二个是对象,则只有第一个操作数的操作结果为 true 时,才会返回该对象
- 如果两个都是对象,则返回第二个
- 如果第一个是 null,则返回 null
- 如果第一个是 NAN,则返回 NAN
- 如果第一个是 undefined,则返回 undefined
- 逻辑或
1 | const result = true || false; //true |
逻辑或的真值表如下:
| 第一个操作数 | 第二个操作数 | 结果 |
| ———— | ———— | —– |
| true | true | true |
| true | false | true |
| false | true | true |
| false | false | false |
和逻辑与类似,在有一个操作数不是布尔值时,就不一定返回布尔值,此时,它遵循以下规则:
- 如果第一个是对象,则返回第一个操作数
- 如果第一个操作数的操作结果为 false 时,则返回第二个操作数
- 如果两个都是 null,则返回 null
- 如果两个都是 NAN,则返回 NAN
- 如果两个都是 undefined,则返回 undefined
关系操作符
大于(>)、小于(<)、大于等于(≥)、小于等于(≤),这几个操作符都返回一个布尔值。和 ECMAScript 中其他操作符一样,当关系操作符使用了非数值时,也要进行数据转换或者完成某些奇怪的操作。以下就是相应的转换规则:
- 如果两个都是数值,则进行数值比较
- 如果两个都是字符串,则比较字符串对应的字符编码
- 如果一个是数值,那将另外的转成数值,然后执行数值比较
- 如果一个是对象,那调用 valueOf()方法,得到的结果按照前面的规则执行比较。如果对象没有 valueOf()方法,则调用 toString()方法,并将得到的结果根据前面的规则进行比较
Tips:NAN 和任何操作数做比较,结果都为 false。这也解释了为什么 undefined>=0 为 false 。
(弱弱的说,其实没找到 null>=’0’ 为什么也是 true,毕竟他没满足前面的场景,’0’ 被莫名其妙的转为了 0)
相等操作符
- 相等和不相等
ECMAScript 中的相等操作符由两个等于号(==)表示,不想等由(!==)这个表示。
在转换不同的数据类型时,相等和不相等都遵循以下基本规则:
- 如果有一个是布尔值,则比较之前先将其转换为数值,false 转为 0,而 true 转为 1
- 然后一个操作数是一个字符串,另一个操作数是数值,在比较相等性之前先将字符串转为数值
- 如果一个是对象,另一个不是,则调用对象的 valueOf()方法,用得到的基本类型值按照前面的规则进行比较
这两个操作符在进行比较时要遵循以下规则:
- null 和 undefined 是相等的
- 要比较相等性之前,不能将 null 和 undefined 转换成其他任何值
- 如果有一个操作数是 NAN,则相等操作符返回 false,不相等操作符返回 true
- 如果两个都是对象,则比较是不是同一个对象。如果指向同一个对象,则返回 true,否则 false
- 全等和不全等
除了在比较之前转换数值之外,全等不全等 和 相等不相等 没什么区别。只有在两个操作数未经转换就相等的情况下返回 true
条件操作符
条件操作符应该算是 ECMAScript 中最灵活的一种操作符了,而且它遵循与 java 中的条件操作符相同的语法形式,如下所示:
1 | var variable = boolean_expression ? true_value : false_value; |
赋值操作符
简单的赋值操作符由(=)表示,其作用就是把右侧的值赋给左侧的变量。
例如:
1 | var num = 10; |
如果在 = 前面再添加乘性操作符、加性操作符或位操作符,就可以完成复合赋值操作。这种复合赋值操作相当于是常规表达式的简写形式。
其他操作符
其他操作符其实还有 乘性操作符、加性操作符、逗号操作符。这些操作符比较简单(懒得写…),想看定义的可以去翻翻红宝书