JS的强制类型转换
将值从一种类型转换为另一种类型通常称为类型转换,这是显式的情况,隐式的情况称为强制类型转换。
JavaScript中的强制类型转换总是返回标量基本类型值,如字符串、数字和布尔值,不会返回对象和函数。
类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时。
我们能够从代码中看出哪些地方是显式强制类型转换,而隐式强制类型转换则不那么明显,通常是某些操作产生的副作用。
1.字符串、数字和布尔值之间类型转换的基本规则
1.1 ToString
基本类型值的字符串化规则为:null
转换为"null",undefined
转换为"undefined",true
转换为"true"。
对于普通对象来说,除非自定义,否则toString()
返回内部属性[[Class]]的值,如"[object Object]"。
var a = [1,2,3]; console.log(a.toString());//"1,2,3"
toString()
可以被显式调用,或者在需要字符串化时自动调用。
JSON.stringify(..)
在将JSON对象序列化为字符串时也用到了ToString。
JSON.stringify(42);//"42" JSON.stringify("42");//""42"" JSON.stringify(null);//"null" JSON.stringify(true);//"true"
所有安全的JSON值都可以使用JSON.stringify(..)
字符串化。安全的JSON值是指能够呈现为有效JSON格式的值。
不安全的JSON值包括undefined、function、symbol和包含循环引用的对象。
如果要对含有非法JSON值的对象做字符串化,或者对象中的某些值无法被序列化时,就需要定义toJSON()
方法来返回一个安全的JSON值。
如果对象中定义了toJSON()
方法,JSON字符串化时会首先调用该方法,然后用它的返回值来进行序列化。toJSON()
返回的应该是一个适当的值(一个能够被字符串化的安全的JSON值),可以是任何类型,然后再由JSON.stringify(..)
对其进行字符串化。
var a = { b : 42, c : "42", d : [1,2,3] }; console.log(JSON.stringify(a,["b","c"]));//"{"b":42,"c","42"}" console.log(JSON.stringify(a,function(k,v){if(k != "c"){return v;}}));//"{"b":42,"d",[1,2,3]}"
1.2 ToNumber
ES5规范定义了抽象操作ToNumber用于将非数字值转换为数字值。
其中true
转换为1,false
转换为0。undefined
转换为NaN
,null
转换为0。
对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。
为了将值转换为相应的基本类型值,抽象操作ToPrimitive会首先检查该值是否有valueOf()
方法,如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用toString()
的返回值(如果存在)来进行强制类型转换。
var a = { valueOf: function(){ return "42"; } }; var b = { toString: function(){ return "42"; } }; var c = [4,2]; c.toString = function(){ return this.join("");//"42" }; Number(a);//42 Number(b);//42 Number(c);//42 Number("");//0 Number([]);//0 Number(["abc"]);//NaN
1.3 ToBoolean
假值
- undefined
- null
- false
- +0、-0和NaN
- ""
假值的布尔强制类型转换结果为false
。
所有的对象都是真值,假值对象除外。
假值对象不是包装了假值的封装对象。
var a = new Boolean(false); var b = new Number(0); var c = new String(""); var d = Boolean( a && b && c ); console.log(d);//true //d为true,说明a、b、c都为true。
浏览器在某些特定情况下,在常规JavaScript语法基础上自己创建了一些外来(exotic)值,这些就是“假值对象”。
真值
真值就是假值列表之外的值。
var a = "false"; var b = "0"; var c = "''"; var d = Boolean( a && b && c ); console.log(d);//true //所有字符串都是真值。
var a = [];//空数组 var b = {};//空对象 var c = function(){};//空函数 var d = Boolean( a && b && c ); console.log(d);//true //[]、{}和function(){}都不在假值列表中,因此它们都是真值。
2.显式强制类型转换
我们在编码时应尽可能地将类型转换表达清楚,以免给别人留坑。类型转换越清晰,代码可读性越高,更容易理解。
2.1 字符串和数字之间的显式转换
var a = 42; var b = String(a);//将数字转换为字符串 var c = "3.14"; var d = Number(c);//将字符串转换为数字 console.log(b);//"42" console.log(d);//3.14 var e = 5+ +c;//+c显式地将c转换为数字 console.log(e);//8.14
日期显式转换为数字
var d = new Date("Mon, 18 Aug 2014 08:53:06 CDT"); console.log(+d);//1408369986000
我们不建议对日期类型使用类型转换,应该使用Date.now()
来获得当前的时间戳,使用new Date(..).getTime()
来获得指定时间的时间戳。
奇特的~运算符
//~x大致等同于-(x+1)。 ~42;//-(42+1) ==> -43 //~和indexOf()一起可以将结果强制转换为真/假值。 //JavaScript中字符串的indexOf(..)方法在字符串中搜索指定的子字符串,如果找到就返回子字符串所在的位置(从0开始),否则返回-1。 var a = "Hello World"; ~a.indexOf("lo");//-4 <-- 真值! if(~a.indexOf("lo")){//true //找到匹配! } //~-1的结果为0。 ~a.indexOf("ol");//0 <-- 假值! !~a.indexOf("ol");//true if(!~a.indexOf("ol")){//true //没有找到匹配! }
字位截除
一些开发人员使用~~
来截除数字值的小数部分,~~
只适用于32位数字。
我们在使用~
和~~
进行此类转换时需要确保其他人也能够看得懂。
2.2 显式解析数字字符串
var a = "42"; var b = "42px"; Number(a);//42 parseInt(a);//42 Number(b);//NaN parseInt(b);//42
2.3 显式转换为布尔值
var a = "0"; var b = []; var c = {}; var d = ""; var e = 0; var f = null; var g; Boolean(a);//true Boolean(b);//true Boolean(c);//true Boolean(d);//false Boolean(e);//false Boolean(f);//false Boolean(g);//false !!a;//true !!b;//true !!c;//true !!d;//false !!e;//false !!f;//false !!g;//false //在if(..)..这样的布尔值上下文中,如果没有使用Boolean(..)和!!,就会自动隐式地进行ToBoolean转换。
3.隐式强制类型转换
隐式强制类型转换的作用是减少冗余,让代码更简洁。
3.1 字符串和数字之间的转换
var a = "42"; var b = "0"; var c = 42; var d = 0; a + b;//"420" c + d;//42 var e = [1,2]; var f = [3,4]; e + f;//"1,23,4" var g = 42; var h = g + ""; h;//"42" var i = "3.14"; var j = i - 0; j;//3.14
3.2 布尔值到数字的转换
function onlyOne(){ var sum = 0; for(var i = 0; i<arguments.length; i++){ if(arguments[i]){ //下面等同于 sum += Number(!!arguments[i]); sum += arguments[i]; } } return sum == 1; } var a = true; var b = false; console.log(onlyOne(b,a));//true console.log(onlyOne(b,a,b,b,b));//true console.log(onlyOne(b,b));//false console.log(onlyOne(b,a,b,b,b,a));//false
3.3 隐式强制类型转换为布尔值
下面的情况会发生布尔值隐式强制类型转换:
-
if(..)
语句中的条件判断表达式。 -
for(..;..;..)
语句中的条件判断表达式。 -
while(..)
和do..while(..)
循环中的条件判断表达式。 -
? :
中的条件判断表达式。 - 逻辑运算符
||
(逻辑或)与&&
(逻辑与)左边的操作数。
3.4 || 和&&
和其他语言不同,在JavaScript中||
和&&
的返回值是两个操作数中的一个(且仅一个)。即选择两个操作数中的一个,然后返回它的值。
var a = 42; var b = "abc"; var c = null; a || b;//42 a && b;//"abc" c || b;//"abc" c && b;//null //a || b相当于a ? a : b //a && b相当于a ? b : a
对于||
来说,如果条件判断结果为true
就返回第一个操作数的值,如果为false
就返回第二个操作数的值。
对于&&
来说,如果条件判断结果为true
就返回第二个操作数的值,如果为false
就返回第一个操作数的值。
4.宽松相等和严格相等
宽松相等==
检查值是否相等,严格相等===
检查值和类型是否相等。==
允许在相等比较中进行强制类型转换,而===
不允许。
4.1 抽象相等
字符串和数字之间的相等比较
var a = 42; var b = "42" a === b;//false a == b;//true
其他类型和布尔类型之间的相等比较
var a = "42"; //不要这样用,条件判断不成立 if(a == true){ //.. } //也不要这样用,条件判断不成立 if(a === true){ //... } //这样的显式用法没问题 if(a){ //.. } //这样的显式用法更好 if(!!a){ //.. } //这样的显式用法也很好 if(Boolean(a)){ //.. }
null和undefined之间的相等比较
var a = null; var b; a == b;//true a == null;//true b == null;//true a == false;//false b == false;//false a == "";//false b == "";//false a == 0;//false b == 0;//false
对象和非对象之间的相等比较
var a = "abc"; var b = Object(a); a === b;//false a == b;//true //a == b结果为true,因为b通过ToPromitive进行强制类型转换(也称为"拆封"),并返回标量基本类型值"abc",与a相等。
var a = null; var b = Object(a); a == b;//false var c = undefined; var d = Object(c); c == d; var e = NaN; var f = Object(a); e == f; //因为没有对应的封装对象,所以null和undefined不能够被封装。 //NaN能够被封装为数字封装对象,但拆封之后NaN == NaN返回false,因为NaN不等于NaN。
4.2 比较少见的情况
假值的相等比较
极端情况
安全运用隐式强制类型转换
- 如果两边的值中有
true
或者false
,千万不要使用==
。 - 如果两边的值中有
[]
、""
或者0
,尽量不要使用==
。
这时最好用===
来避免不经意的强制类型转换。这两个原则可以让我们避开几乎所有强制类型转换的坑。
5.抽象关系比较
var a = [42]; var b = ["43"]; a < b;//true b < a;//false var c = ["42"]; var d = ["043"]; c < d;//false var e = [4,2]; var f = [0,4,3]; e < f;//false //e转换为"4,2",f转换为"0,4,3",按字母顺序进行比较
var a = {b:42}; var b = {b:43}; a < b;//false a == b;//false a > b;//false a <= b;//true a >= b;//true //根据规范a <= b被处理为b < a,然后将结果反转,因为b < a的结果是false,所以a <= b的结果是true。
参考资料:《你不知道的JavaScript》(中卷) 第四章
上一篇: MySQL(九)
下一篇: golang自定义路由控制实现(一)