null在关系运算中的坑 & 关系运算符的隐式转换问题
注意: 比较运算符 和 相等运算符 的 ECMAscript 语法实现不同。
比较运算符 和 相等运算符 对数据进行了隐式转换,
相当于调用了 Number(), '' false 转换为 0; true 转换为 1。
console.log('' >= 5); // false
console.log('' < 5); // true 相当于 0 < 5
console.log('' == 0); // true console.log(true < 3); // true 相当于 0 < 3
console.log(false > -2); // true 相当于 0 > -2
console.log(true == 1); // true
console.log(false == 0); // true
但是,null 比较特殊,比较运算符 >= <= > < 会对其进行数据类型转换; 相等运算符 == != 不会对其进行数据类型转换。
// null 比较特殊,比较运算符 >= <= > < 会对其进行数据类型转换;
// 相等运算符 == != 不会对其进行数据类型转换
console.log(null <= 0); // true
console.log(null >= 0); // true
console.log(null == 0); // false 填坑
console.log(null - 0); // 0 console.log(null == undefined); // true
参考链接:
https://www.jianshu.com/p/97a8f9c10572
https://es6.ruanyifeng.com/?search=逗号&x=4&y=9#docs/spec#相等运算符
1.前言
今天看见朋友们在讨论一个问题,说 null 到底和 0 是不是相等的。
听到这里,自己赶紧去写个 Demo 试一下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MR_LP:3206064928</title>
</head>
<body>
</body>
<script type="text/javascript">
console.log(null > 0); // false
console.log(null < 0); // false
console.log(null >= 0); // true
console.log(null <= 0); // true
console.log(null == 0); // false
console.log(null === 0); // false
</script>
</html>
什么情况?
为什么 console.log(null <= 0);
和 console.log(null >= 0);
这两条的判断是 true 呢?
2.查阅资料
如果想明确,这个问题具体是怎么回事,那么我们需要重新来回顾一下我们的 ECMAScript Language Specification (HTML version),翻译过来就是ECMAScript语言规范(HTML版本)。
2.1 内部相等性运算算法
首先我们来看一下 ES3 关于 内部相等性运算的算法实现。
11.9.3 The Abstract Equality Comparison Algorithm
The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:
- If Type(x) is different from Type(y), go to step 14.
- If Type(x) is Undefined, return true.
- If Type(x) is Null, return true.
- If Type(x) is not Number, go to step 11.
- If x is NaN, return false.
- If y is NaN, return false.
- If x is the same number value as y, return true.
- If x is +0 and y is -0, return true.
- If x is -0 and y is +0, return true.
- Return false.
- If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions). Otherwise, return false.
- If Type(x) is Boolean, return true if x and y are both true or both false. Otherwise, return false.
- Return true if x and y refer to the same object or if they refer to objects joined to each other (see 13.1.2). Otherwise, return false.
- If x is null and y is undefined, return true.
- If x is undefined and y is null, return true.
- If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
- If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x)== y.
- If Type(x) is Boolean, return the result of the comparison ToNumber(x)== y.
- If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
- If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
- If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x)== y.
- Return false.
2.2 内部关系运算算法
接下来我们再来看一下 ES3 关于 内部关系运算的算法实现。
11.8.5 The Abstract Relational Comparison Algorithm
The comparison x < y, where x and y are values, produces true, false, or undefined (which indicates that at least one operand is NaN). Such a comparison is performed as follows:
- Call ToPrimitive(x, hint Number).**
- Call ToPrimitive(y, hint Number).**
- If Type(Result(1)) is String and Type(Result(2)) is String, go to step 16. (Note that this step differs from step 7 in the algorithm for the addition operator **+
** in using and instead of or.) - Call ToNumber(Result(1)).
- Call ToNumber(Result(2)).
- If Result(4) is NaN, return undefined.
- If Result(5) is NaN, return undefined.
- If Result(4) and Result(5) are the same number value, return false.
- If Result(4) is +0 and Result(5) is -0, return false.
- If Result(4) is -0 and Result(5) is +0, return false.
- If Result(4) is +∞, return false.
- If Result(5) is +∞, return true.
- If Result(5) is -∞, return false.
- If Result(4) is -∞, return true.
- If the mathematical value of Result(4) is less than the mathematical value of Result(5) --- note that these mathematical values are both finite and not both zero --- return true. Otherwise, return false.
- If Result(2) is a prefix of Result(1), return false. (A string value p is a prefix of string value q if q can be the result of concatenating p and some other stringr. Note that any string is a prefix of itself, because r may be the empty string.)
- If Result(1) is a prefix of Result(2), return true.
- Let k be the smallest nonnegative integer such that the character at position k within Result(1) is different from the character at position k within Result(2). (There must be such a k, for neither string is a prefix of the other.)
- Let m be the integer that is the code point value for the character at position k within Result(1).
- Let n be the integer that is the code point value for the character at position k within Result(2).
- If m < n, return true. Otherwise, return false.
2.3 ES3 的 运算符
2.3.1 ES3 的 ">" 运算符:
The Greater-than Operator ( > )
The production RelationalExpression :
RelationalExpression > ShiftExpression is evaluated as follows:
- Evaluate RelationalExpression.
- Call GetValue(Result(1)).
- Evaluate ShiftExpression.
- Call GetValue(Result(3)).
- **Perform the comparison Result(4) < Result(2). **
- If Result(5) is undefined, return false. Otherwise, return Result(5).
2.3.2 ES3 的">=" 运算符:
The Greater-than-or-equal Operator ( >= )
The production RelationalExpression :
RelationalExpression >= ShiftExpression is evaluated as follows:
- Evaluate RelationalExpression.
- Call GetValue(Result(1)).
- Evaluate ShiftExpression.
- Call GetValue(Result(3)).
- Perform the comparison Result(2) < Result(4). (see 11.8.5).
- If Result(5) is true or undefined, return false. Otherwise, return true.
2.3.3 ES3 的 "==" 运算符 :
The Equals Operator ( == )
The production EqualityExpression :
EqualityExpression == RelationalExpression is evaluated as
follows:
- Evaluate EqualityExpression.
- Call GetValue(Result(1)).
- Evaluate RelationalExpression.
- Call GetValue(Result(3)).
- Perform the comparison Result(4) == Result(2). (see 11.9.3).
- Return Result(5).
3. 根据资料得出的内容
着重看一下,上面特意加粗的地方,我们可以明确下面三件事。
关系运算符 和 相等运算符 并不是一个类别的.
关系运算符,在设计上,总是需要运算元尝试转为一个number . 而相等运算符在设计上,则没有这方面的考虑.
最重要的一点, 不要把 拿 a > b , a == b 的结果 想当然的去和 a >= b 建立联系. 正确的符合最初设计思想的关系是 a > b 与 a >= b是一组 . a == b 和其他相等运算符才是一组. 比如 a === b , a != b, a !== b .
那么我们就可以反过来看这个问题了。
null > 0 // null 尝试转型为number , 则为0 . 所以结果为 false,
null >= 0 // null 尝试转为number ,则为0 , 结果为 true.
null == 0 // null在设计上,在此处不尝试转型. 所以 结果为false.
这里引用一下 Franky大大的话。
a >= b 运算符只是简单的去对 a < b的结果取反. 我以为这是一个设计上的失误的另一个理由是 undefined,在标准中,被单拎出来.细心的你,也一定发现了这一点. 对于undefined的设计, undefined > 0 , undefined < 0, undefined == 0 的结果是符合设计上,逻辑的一致性的. 而null是被遗漏的东西.直到今天早上.我重新翻阅了ES3,5.相关章节. 才恍然大悟自己没有从根本上理解到这个问题.
虽然前面的例子,我catch到了BE当初的设计思想. 但是从全局的角度来看. 从关系运算符到相等运算符,尤其是相等运算符的设计上. 真的十分混乱不堪. BE在信中提到,他对 == 的现状也很无奈. 甚至用愚蠢这个词来形容自己当初的实现(当然他还提到,当初只是为了在10天内设计出js,并跑过qa的测试用例). 即使如此, 但是他仍然表示 null == 0 这个结果是他想要的.
好吧,到了这里,我也有种无力感. 我认为纵观javascript,对关系运算和相等运算的设计.除了混乱,我想不出还有什么词来形容它们更恰当. 这一点从,我们生产环境代码中,大量的类型检查,和防御性代码的的存在,就可以证明这一点.
同时 Franky大大还举了另外一个例子。
function case1(a){
if(a == null){
....
}
}
function case2(a){
if(a == undefined){
...
}
}
// 上面两组完全等价, 这就是一种不明确表述.
// 我们永远不知道代码编写者的目的到底是同时匹配null 和 undefined还是只匹配其中某一个
function case3(a){
if(a === null || a === undefined){
...
}
}
// case3 才是最好的表述. 我们明确知道代码编写者的意图.
// 即使很多人可能认为这个代码很愚蠢. 但我坚定的认为这才是好代码.
最后, 不得不提到,我发出null >= 0 这封信后, Andrea Giammarchi 表示了对我之前看法的支持,他同我最初的看法一样,认为 null >= 0 的结果应该为 false . 并建议在 ES7 中的严格模式中,修改这个结果. 虽然同样遭到 David Bruant 的反对. 好吧为他和我的这个错误看法,默哀一分钟...
4.后记
所以写代码,写规范,都应该明确表述. 即使表述的很罗嗦,但不会引起歧义或怀疑. 这才是一份好的标准.文档,代码. 而避免歧义,和各种混乱不堪的规则,是一门语言最应遵守的设计原则.
最后也希望大家在日常的开发中,能够少遇坑。
作者:MR_LIXP
链接:https://www.jianshu.com/p/97a8f9c10572
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
null的坑 和 比较运算符、相等运算符的隐式转换问题 (在javascript中,null>=0 为真,null<=0 为真,null==0却为假,null到底是什么?)的更多相关文章
-
js学习日记-隐式转换相关的坑及知识
隐式转换比较是js中绕不过去的坎,就算有几年经验的工程师也很有可能对这块知识不够熟悉.就算你知道使用===比较从而避免踩坑,但是团队其它成员不一定知道有这样或那样的坑,有后端语言经验的人常常会形成一个 ...
-
C++ operator重载运算符和隐式转换功能的实现
C++ operator重载运算符和隐式转换功能的实现: #include <iostream> using namespace std; class OperatorTest { pub ...
-
无法确定条件表达式的类型,因为“<;null>;”和“System.DateTime”之间没有隐式转换----解决办法
例子:(报错了) public DateTime? time { get; set; } time = item.HospOutDate.HasValue ? DateTime.Parse(item. ...
-
工作总结 无法确定条件表达式的类型,因为“<;null>;”和“System.DateTime”之间没有隐式转换 解决办法 object——Nullable<;T>; (可空类型)
可空值类型 备注 一种类型认为是可以为 null,如果它可以分配一个值,也可以分配null,这意味着类型具有无论如何没有值. 默认情况下,所有都引用类型,如String,是否可以为 null, ...
-
MySQL隐式转换的坑
MySQL以以下规则描述比较操作如何进行转换: 两个参数至少有一个是 NULL 时,比较的结果也是 NULL,例外是使用 <=> 对两个 NULL 做比较时会返回 1,这两种情况都不需要做 ...
-
C# 转换运算符:implicit(隐式),explicit(显示)
//A类 class Cls1 { public string name; //构造函数 public Cls1(string name) { this.name = name; } //implic ...
-
JavaScript中涉及得运算符以及运算符的优先级
在js中主要有三种运算符:算术运算符,逻辑与比较运算符,位运算符.在着三种运算符中,最常见的应该是算术与比较运算符,位运算符比较少见一些 *说到了运算符,就不得不说运算符的优先级.下面我来列一下这些运 ...
-
c/c++ 重载运算符 类型转换运算符
重载运算符 类型转换运算符 问题:能不能把一个类型A的对象a,转换成另一个类型B的对象b呢?? 是可以的.这就必须要用类型A的类型转换运算符(conversion operator) 下面的opera ...
-
JavaScript 引入方式 语言规范 语言基础 数据类型 常用方法 数组 if_else 比较运算符 for while 函数 函数的全局变量和局部变量 {Javascript学习}
Javascript学习 JavaScript概述 ECMAScript和JavaScript的关系 1996年11月,JavaScript的创造者--Netscape公司,决定将JavaScript ...
随机推荐
-
excel2013添加坐标轴名称label
图画好了,x.y轴没有名称,怎么办那 点击左上角有个---添加图标元素----里面有轴标题应该就是
-
SQL SERVER 2008安装错误(is not a valid login or you do have permission)
在网上搜索查找问题: 原因:[计算机名] 与[账号名称]名称一致. 解决方案: 修改计算机名 桌面 -> 我的电脑 -> 右鍵点击属性 -> 选择计算机名选项卡 -> 更改 - ...
-
MYSQL procedure
没怎么接触过mysql procedure,今天建个calendar表还磨磨唧唧的,记录一下: CREATE PROCEDURE `new_procedure` (start_date DATA,en ...
-
asterisk 语音文件转换
Centos wav to sln sox foo-in.wav -t raw -r 8000 -s -2 -c 1 foo-out.sln 当前目录下所有语音wav文件 转换成sln for a i ...
-
Linux上构建一个RADIUS服务器详解
作为一名网络管理员,您需要为您所需管理的每个网络设备存放用于管理的用户信息.但是网络设备通常只支持有限的用户管理功能.学习如何使用Linux上的一个外部RADIUS服务器来验证用户,具体来说是通过一个 ...
-
ExperDot的博客目录导航
最近活动 我更新了博客!粒子系统:从零开始画一棵树 Github:[ UWP ] [ JavaScript ] 自然编程 奇幻元纪 上帝创世篇:如何画一颗静态树 女娲补天篇:仿人工拼接碎片 吴刚伐桂 ...
-
linux(centos)搭建.net core 运行环境
总的来说,非常简单,我记录一下: 1.打开https://www.microsoft.com/net/download?initial-os=linux 这里"Instal .NET C ...
-
php删除文件夹
function deldir($dir) { $dh=opendir($dir); while ($file=readdir($dh)) { if($file!="." & ...
-
ansible 快速入门
安装 $ sudo apt-get install software-properties-common $ sudo apt-add-repository ppa:ansible/ansible $ ...
-
Replication容量和错误日志
gtid排错 set sql_log_bin=off; #人为关闭二进制日志