null的坑 和 比较运算符、相等运算符的隐式转换问题 (在javascript中,null>=0 为真,null<=0 为真,null==0却为假,null到底是什么?)
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 ...
- 一个 MySQL 隐式转换的坑,差点把服务器整崩溃了
我是风筝,公众号「古时的风筝」,专注于 Java技术 及周边生态. 文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面. 本来是一个平静而美好的下午,其 ...
- 无法确定条件表达式的类型,因为“<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 ...
随机推荐
- c语言的自动类型转换(转)
一.自动转换遵循以下规则: 若参与运算量的类型不同,则先转换成同一类型,然后进行运算. 转换按数据长度增加的方向进行,以保证精度不降低.如int型和long型运算时,先把int量转成long型后再进行 ...
- debug:am、cmd命令源码分析
debug:am.cmd命令源码分析 目录 debug:am.cmd命令源码分析 am命令的实现 手机里的am am.jar cmd命令的实现 手机里的cmd cmd activity cmd.cpp ...
- Bigdecimal用法
一.简介 Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算.双精度浮点型变量double可以处理16位有效数.在实际应用中,需要对更大或者更 ...
- Tomcat修改最大连接数及查看最大连接数
一.背景 公司进行安全整改, 技术要求:会话限制:应能够对应用系统的最大并发会话连接数进行限制: 提供凭证:提供对系统最大并发会话连接数进行限制的截图,需要将所有被检查系统中间件配置截图,如果不限制最 ...
- Python基础之获取路径与切换路径
一直以为我写了关于路径有关的博客,看了一圈才发现没写,那么现在就来整理下. 一.获取当前路径 os.getcwd() 二.获取当前文件路径:(__file__是当前执行文件) os.path.absp ...
- SpringBoot添加Cors跨域配置,解决No 'Access-Control-Allow-Origin' header is present on the requested resource
目录 什么是CORS SpringBoot 全局配置CORS 拦截器处理预检请求 什么是CORS 跨域(CORS)请求:同源策略/SOP(Same origin policy)是一种约定,由Netsc ...
- vue 跨域 springCloud @CrossOrigin注解
vue 跨域 springCloud @CrossOrigin注解 一丶什么是跨域 跨域问题来源于浏览器的同源策略,浏览器为了提高网站的安全性,在发送ajax请求时,只有在当前页面地址与请求地址的协 ...
- element+vue点击新增表格内在已有数据添加一行带输入框内容
在element+vue项目中,需求要表格内已有数据,点击新增在首行添加一行带输入框内容 table的数据为datas=[],那么下面是一列的数据,多列可循环或复制 <el-table-colu ...
- intouch 开发源程序加密方法
在先前项目中,因为同行竞争被拷贝走了源程序代码,以至于被上司责备,故而亡羊补牢对intouch(10.1老版本进行源代码加密探索)整理方法如下. 1.intouch wondermarker打开源程序 ...
- Apache ActiveMQ(cve-2015-5254)
影响版本 Apache ActiveMQ 5.13.0之前5.x版本中存在安全漏洞 复现 使用工具执行命令 工具地址 https://github.com/matthiaskaiser/jmet/re ...