首先在这之前,我并不知道undefined == null
且 undefined !== null
,如果你也不知道,那么可以继续阅读试着了解,如果已经知道就忽略此文吧
本文所述内容:
-
undefined
与null
的区别 -
javascript
数据类型转换 -
==
与===
的区别(使用==
时发生了什么)
1. undefined
与null
的区别
我首先看了这篇文章:理解Javascript_02_理解undefined和null
个人总结:虽然觉得这篇文章的有些理解是错的,但是给我很好的开了个头。他的表述是:
- Undefined代表没有赋值的基本数据类型
- Null代表没有赋值的引用数据类型
我们都知道,基本数据类型数据存在栈里,引用数据类型数据存在堆里,栈比堆运算速度快,堆比栈存的多。这篇文章认为,为了的这个计算效率原因,再加上“undefined 实际上是从值 null 派生来的”,基本数据类型是对应引用数据类型的子类,只不过是为了提高效率,将其放在栈内存中而已。这是错误的理解。
基本数据类型也就是原始数据类型(primitive),引用数据类型就是复合数据类型(complex),“undefined 实际上是从值 null 派生来的”个人觉得这句话只是说的字面上的派生,之所以这么理解undefined和null
,是因为“历史原因”,看了下百科关于JavaScript的发明人——Brendan Eich,里面有说他的设计思路:
javascript为什么要用2个表达方式来表述空这个概念?摘一段引用:
1.null 和 undefined在现代JS语义里面是有明确区别的:
- null 表示一个值被定义了,定义为“空值”;
- undefined 表示根本不存在定义。
所以设置一个值为 null 是合理的
2.JS 中同时存在 undefined 和 null 是合理的
首先在 Java 中不存在 undefined 是很合理的:Java 是一个静态类型语言,对于 Java 来说不可能存在一个“不存在”的成员(不存在的话直接就编译失败了),所以只用 null 来表示语义上的空值。而 JavaScript 是一门动态类型语言,成员除了表示存在的空值外,还有可能根本就不存在(因为存不存在只在运行期才知道),所以这就要一个值来表示对某成员的 getter 是取不到值的。
java中null只是存粹的表示空,且不是一种任何数据类型,作为动态类型的js来说,有些值只有在运算时才能知道存不存在,必须要有一个类型来表示,所以就出现了undefined。另外js里的:typeof null === "object"
应该是一个设计失误。
此处参考undefined与null的区别@ RedNax评论
最后undefined == null
,就是规范定义的结果,规范第2条中说:If x is null and y is undefined, return true.
此处规范中文版,所以说undefined == null
并没有什么可讨论的,规范就是这么定义的,至于undefined !== null
因为这就是2个不同类型的数据,肯定不相等。
2.javascript数据类型转换
讨论类似undefined == null
,[] !== []
有何实际意义?其实,我们讨论这些问题一定要去了解其中涉及的原理,讨论单个点确实无意义。就像第一个问题:涉及的点就有类型转换和数据类型和内存管理。
我们这里讨论数据类型转换内容:
- parseInt()和parseFloat()
- 强制转换
- 隐式转换
parseInt()和parseFloat()
内容 | parseInt | parseFloat |
---|---|---|
基数 | 默认十进制,八进制,十六进制可选 | 只能是十进制 |
共同点 | 都是将有效字符前的字符转化为数字 | 都只有对String类型调用这些方法,其他类型或者该字符串不是一个有效的字符串都是返回NaN
|
强制转换
强制转换主要指使用Number、String和Boolean三个构造函数,手动将各种类型的值,转换成数字、字符串或者布尔值。
此处查看数据类型转换即可
里面未涉及的点:valueOf()
和 toString()
,每个对象都有自己的valueOf()
和 toString()
,并且做类型转换时自动调用。
1.Object.prototype.valueOf() Object.prototype.toString()
默认情况下,对象的valueOf方法返回对象本身
toString方法返回returns "[object type]"
,这里的type为object
Every object has a toString() method that is automatically called when the object is to be represented as a text value or when an object is referred to in a manner in which a string is expected.
2.Array.prototype.valueOf() Array.prototype.toString()
同样,数组的valueOf方法返回的也是它本身
let arr = [1, 3, 4]
arr.valueOf() // [1, 3, 4]
toString返回的是A string representing the elements of the array.
let arr = [1, 2]
arr.toString() // 1,2
3.Function.prototype.valueOf() Function.prototype.toString()
函数的valueOf方法返回的也是它本身
这里的toString返回A string representing the source code of the function.
由数据类型转换一文可知:
做Number强制转换并且参数是对象时,转换经历了3个步骤:
如果valueOf返回的是基本数据类型,则直接用基本数据类型的转换规则;如果返回的是对象,则再调用toString,得到string基本数据类型后,再转换。
对于一般的3中引用类型的数据,valueOf都会返回其本身,除非重写valueOf方法。简单来说,Number做强制转换时,直接用toString方法后再做转换就好了。
同理,String做强制转换时,是先调用的toString,如果返回的不是基本数据类型,则再调用valueOf。所以说,一般情况下valueOf都是返回的本身,没啥用,除非重写valueOf,做实事的都是toString
对于Boolean,所有对象都返回true。
总结:我们了解Boolean([])
,String([])
有何实际意义?真正用到的场景我想就是隐式转换了吧
3.== 与 ===的区别(隐式转换)
我们都知道,在做==比较时,不同类型的数据会先转换成一致后再做比较:
'3' == 3 // true
0 == false // true
在做===时,如果类型不一致就直接返回false了,一致的才会继续比较
其实,隐式转换包含很多场景,除了上面的==外,还有
1 + '3' // '13'
let a = 2 > '1' ? 'string' : false
'4' - '2'
+new Date
+ {a: 1} // NaN
隐式转换,基本类型之间到底是谁转谁呢?
就拿==来说,有以下几种情况n1 == n2
:
- 如果有一个操作数为boolean值,则先转为数值后再做比较;
- 如果n1是字符串,n2是数值,也是转换为数值再比较;
- 如果有一个是对象,一个不是,则先调用Number做强制转换,得到基本数据类型后再比较;
也就是说,如果n1,n2类型不一致(出现boolean或数值),会优先转为数值后再进行比较。
false == 1 // false 转为0再比较
false == '2' // false转为0,'2'转为1
'3' == 2 // '3'转为3再比较
其他除了==情况下,都是根据实际情况来定,比如:
- 在if或者三目运算符中,那肯定是转为boolean了,除了以下六个值为false,其他都是自动转为true
undefined
null
-0
0或+0
NaN
''(空字符串)
详细参考数据类型转换.
关于==时的隐式类型转换规则时,聊一聊 JS 中的『隐式类型转换』跟我的想法一致:
总结
了解了这么多,无非就是为了知道语言自身的规则,好让我们能更好的规避错误。
当然里面涉及的点,还是很有必要了解与熟知的:js数据类型
,强制转换
,js历史
。
说到隐式转换,建议在所有使用条件判断的时候都使用全等运算符 === 来进行条件判断,全等运算符会先进行数据类型判断,并且不会发生隐式类型转换。