我们写JavaScript肯定会涉及到各种类型转换,有些类型转换简单易懂,但有些转换却会意想不到,甚至带来很多的麻烦。
所以我这篇学习笔记旨在梳理一下这种转换,加强一下自己的理解,也方便大家查阅一些问题,如文章中有不妥之处请帮忙指正。
下面开始正文:
1.类型
1.1 内置类型
JS有七种类型:
1.空值(null)
2.未定义(undefined)
3.字符串(string)
4.数字(number)
5.布尔(boolean)
6.对象(object)
7.符号(symblo,ES6新增)
除对象(object)外,其他都是基本类型
1.2 类型判断
使用 typeof 来查看对应值的类型
typeof undefined //undefined
typeof true //boolean
typeof 12 //number
typeof "12" //string
typeof {num:12} //object
typeof Symbol() //symbol
以上六种类型均符合我们预期
typeof null //object
但是null却有些特殊,它的返回结果并不是如我们所想。
null是唯一 一个用typeof检测会返回"object"的基本类型值。
1.3 undefined 和 undeclared
有时候开发时会经常犯一些理解性错误,将undefined和undeclared等同起来,但其实是完全两回事。
var a
console.log(a) //undefined
console.log(b) //ReferenceError: b is not defined
console.log(typeof a) //undefined
console.log(typeof b) //undefined
已在作用域中声明但是没有赋值的变量是undefined,相反没有在作用域中声明的变量是undeclared.
但是如例子中,虽然两个变量情况不同,但是typeof对于undefined和undeclared的处理方式都是相同的,全为undefined,所以看着真的好像啊emmm~~~
虽然b是一个Undeclared变量,但是typeof b不会报错,这是因为typeof有一个特殊的安全防范机制
1.4 undefined,null和{}
开发过程中,发现经常出现这三种情况,现在从不同方面进行比较
console.log(typeof undefined) //undefined
console.log(typeof null) //object
console.log(typeof {}) //object
console.log({} === null) //false
console.log(Boolean({})) //true
console.log(Boolean(null)) //false
console.log(Number({})) //NaN
console.log(Number(null)) //0
console.log(Number(undefined)) //NaN
如上面所说,从类型能看出,undefined的类型是undefined,但是null和{}都是object。
另外,null和{}是不相同的,null值表示一个空对象指针,如果打算定义一个变量准备在将来用于保存对象,可以将变量初始化为null。 {}其实是一个对象,经历过声明和初始化,只不过里面没有存储数据,是一个空对象。
进行布尔值转换时,{}是为true的,而null是为false,在开发中有使用到这两个时候,还是要注意的。(我是踩过这个坑的~~~,挺疼的)
2.强制类型转换
将值从一种类型转换到另一种类型通常称为强制类型转换,而将转换发生的阶段不同,暂且将它们命名为“隐式强制类型转换”和“显式强制类型转换”
const a = 11
const b = a + '' //隐式强制类型转换
const c = String(a) //显示强制类型转换
对变量b来说,进行的是隐式转换,由于 +运算符的其中一个操作数是字符串,所以是字符串拼接操作,将数字11隐式强制换行为字符串“11”
对应变量c,使用String的方法则显式强制转换为字符串
2.1 显式强制转换
2.1.1 字符串和数字之间转换
字符串和数字之间的转换通过String()和Number()这两个方法来进行
const a = 11
const b = String(a) // "11"
const c = "111"
const d = Number(c) // 111
2.1.2 显示解析数字字符串
解析字符串中的数字和将字符串强制类型转换为数字的返回结果都是数字,但是解析和转换是有区别的
const a = "42"
const b = "42ha1"
console.log(Number(a)) //42
console.log(parseInt(a)) //42
console.log(Number(b)) //NaN
console.log(parseInt(b)) //42
解析允许字符串中含有非数字字符,解析会从左到右的顺序,遇到非数字字符停止。
转换不允许出现非数字字符,否则会失败并返回NaN
备注:parseInt是针对字符串的,转其他类型不可以
2.1.3 显示转换为布尔值
使用Boolean()将非布尔值强制转换为布尔值,但这个不常用
const a = 0
const b = 1
console.log(Boolean(a)) //false
console.log(Boolean(b)) //true
常用的方法为!!
const a = 0
const b = 1
console.log(!!a) //false
console.log(!!b) //true
另外,我们在开发中经常会这么写:
const instance = {}
if(this.instance) {/*进行的操作*/}
像在if()这样的上下文中,如果没有使用Boolean()和!!进行显示强制转换,便会自动隐式进行ToBoolean的转换。
如果这时你想判断的是当instance有数据时进行某些操作,使用这种写法就不会达成你的需求,因为Boolean({})是为true的,采用这样写法永远都会执行里面的操作,要小心
2.2 隐式强制转换
采用隐式强制转换会让代码更加简洁,减少冗余 ,但是问题也不少,所以需要好好了解这种转换。(吐槽一句:隐式转换真的好懵圈,一定要耐心研究,坑挺多的~~)
2.2.1 字符串和数字之间的隐式强制类型转换
JS中常会用到+运算符进行一些操作,包括数字运算和字符串拼接,如图:
let a = "12"
let b = "3"
let c = 12
let d = 3
console.log(a+b) //"123"
console.log(b+c) // "312"
console.log(c+d) // 15
对上面例子普遍理解就是,因为“12”和“3”都是字符串,所以进行+执行时候,只要某一个或两个操作数是字符串,就进行字符串拼接操作。这个理解没有错,但不全,实际情况更加复杂。
接下来看一个例子:
let e = [1,2]
let f = [3,4]
console.log(e+f) //"1,23,4"
图中是两个数组,没有字符串,但是进行+执行时,他们都被进行强制转换为字符串,然后进行拼接操作。
原因如下:
根据ES5规范11.6.1节:如果某个操作时字符串或者能够通过以下步骤转换为字符串,+将进行拼接操作。
(本人吐槽:规范太难理解,知道怎么用就好了吧~~)
2.2.2 隐式强制转换为布尔值
布尔值的隐式转换时最为常见的,也是最容易出问题的
常见几种类型:
1.if(…)语句中的条件判断;
2.for(…;…;…)语句中第二个的条件判断
3.while(…)语句中的条件判断
4. ?:中的条件判断
2.2.3 ||和&&
逻辑运算符也是我们经常使用的,但很多人都认为使用逻辑运算符返回的结果是布尔值,但其实并不是,它们的返回值是两个操作数其中一个的值。
详见几个例子:
let a = 12
let b = "abc"
let c = null
console.log(a && b) // "abc"
console.log(a || b) // 12
console.log(a && c) // null
console.log(a || c) // 12
console.log(c && b) // null
console.log(c || b) // "abc"
以(a && b)
和(a || b)
为例进行说明:
|| 和 &&首先会对第一个操作数(a)进行条件判断,如果不是布尔值,就先进行布尔强制转换,然后再执行条件判断。
&&:如果第一个操作数条件判断结果为true,就返回第二个操作数(b)的值,如果结果为false,就返回第一个操作数(a)
|| :它和&&正好相反,如果第一个操作数条件判断结果为true,就返回第一个操作数(a)的值,如果结果为false,则返回第二个操作数(b)
另外,由于&&这种特性,在开发时候就可以用前面的表达式为后面的表达式“把关”,例如
price.totalPrice && price.totalPrice.toFixed(2)
在 这个代码中,首先检验price下面的totalPrice是否存在,如果存在进行后面toFixed的操作,如果totalPrice不存在,就不进行后续操作了,防止toFixed的报错。
使用这种写法就更加简洁,就不用if(price.totalPrice) {…}这种判断写法了。
最后在看一个例子:
let a = 42
let b = "abc"
let c = null
if (a && (b || c)) {
console.log('hi')
}
通过本节会知道,a && (b || c)
的结果是“abc”,而非true,但是在2.2.2节中说到,if(…)语句中的条件判断会进行隐式强制转换为布尔值,也就是将“abc”强制转换,所以结果值为true。通过这些隐式强制转换,使得条件判断没有发生过问题。
2.2.4 宽松相等和严格相等
宽松相等== 和 严格相等=== 有什么区别是面试经常提问题,很多面经里面会说 “==是检查值是否相等,===是检查值和类型是否相等”,但其实并不准确。
正确解释: ==允许在相等比较中进行强制转换,但是 ===不允许
2.2.4.1 其他类型和布尔类型之间的相等比较
来一个好玩的例子
let a = "42"
let b = true
console.log(a == b) // false
console.log(a == c) // false
会不会有疑问啊?“42”明明就是真值,为什么和true不相等呢?
在规范11.9.3.4-7提及:
(1)If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
(2)If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
(3)Type(x) is String and Type(y) is Number,
return the result of the comparison ToNumber(x) == y.
所以再看刚才例子:
Type(true)是Boolean,进行ToNumber转换为1,变成“42”==1,二者类型不同 ,继续转换,Type(“42”)为String,转换为42,所以最后对比的是 42 == 1,结果为false。
(是不是很懵啊~~反正我是晕了,为了自己心里健康,建议这种就是玩玩就好,真正开发还是别写了,别和 true和 false进行判断!!)
使用if(a){…}的写法不香么?
或者if(!!a){…}也不错啊
2.2.4.2 安全使用隐式强制转换
在使用==时候,需要对两边的值进行推敲:
1.当两边的值有true和false,千万不要使用 ==
2.当两边的值有[],"",和0,最好不要使用 ==
至于其他情况,按照自己需求使用。
(个人观点:有时候开发时候使用 ==编辑器都会报提醒建议使用 ===,很多人也会说直接使用 ===,其实到底使用哪种相等完全取决于自己需求使用,在了解转换的规则之后,宽松相等也会带来简单的开发吧)
2.3 好玩的例子(一定要看)
console.log(String({})) //“[object Object]”
console.log(Number(undefined))//NaN
console.log(Number([]))//0
console.log({} + []) //“[object Object]”
console.log([] + {}) //“[object Object]”
console.log({} + 0) //“[object Object]0”
console.log('0' + undefined) //“0undefined”
console.log(0 + undefined) //NaN
最后玩几个小例子,第4,5,6,7个按照上面讲的,将他们分别进行String隐式强制转换,然后以字符串形式拼接。而第8个是进行了Number的隐式强制转换。
再看一个在控制台的输出:
但是底下这个{}+[]结果为什么就和上面的例子不一样了,不是“[object object]”?
这是因为在第一行代码中,{}是被当作一个独立的空代码块(不进行任何操作),最后+[],将[]隐式强制转换为0。
光靠 JavaScript 语法就无法阐述语句 {} 到底是一个代码块,还是一个对象。为了解决这个问题,ECMA
的方法十分简单粗暴:在语法解析的时候,如果一个语句以{}开头,就把它解释成代码块
但是在第二行代码中,{}出现在+运算表达式中,被作为一个值(空对象)来处理,所以它的处理方式和上面例子相同,将[]和{}都转为字符串。
2.4 好玩例子补充-eval学习(2020.6.5)
今天发现用控制台输出时候,经常会有undefined的显示,之前一直不知道什么原因,现在终于知道原因了!
首先一定要了解,Chrome的控制台是用eval来执行的
那么什么是eval? eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码
eval()执行的原理:
eval()只接受原始字符串作为参数,
如果 string 参数不是原始字符串,那么该方法将不作任何改变地返回。
如果参数是字符串,它会把字符串当成JavaScript代码进行编译
如果编译失败,抛出一个语法错误异常。
如果编译成功,则开始执行这一段代码,并返回字符串中的最后一个表达式会或语句的值,
如果最后一个表达式或语句没有值,则最终返回undefined。
如果字符串抛出一个异常,这个异常将把该调用传递给eval()。
所以输入 var a = 1时,这个表达式没有值,则返回了undefinded
如果输入const sum = eval('10*10+5')
,这个表达式会有返回值,会返回105
通过这回了解了eval是什么,也了解了undefinde为什么会出现了
(各种神奇例子很多,大家发现好玩的,可以一并留言哦~)