对于数据类型无论是对哪一种语言都是非常重要的,虽然JavaScript是一种若类型语言,但是几乎所有的JavaScript程序都会涉及某种形式的强制类型转换,因此了解JavaScript的类型还是非常重要的。
JavaScript中有七种内置的类型:
- Null
- Undefined
- Boolean
- Number
- String
- Object
- Symbol(符号,ES6新增)
除了Obeject之外其他六种类型又统称为“基本类型”。
JavaScript 是一门非常灵活的语言,几乎所有的值都可以用这7种类型表示。除了Null之外,其他的六种类型都可以使用typeof运算符来查看,而Null使用typeof返回的不会是null,而是一个object,这个bug由来已久,其涉及JavaScript中的“假值”问题,后面会对其进行讲解。
类型之间的转换
JavaScript中只有值才有类型这个概念的,变量与对象都是没有的,所以在说到类型转换的时候都是基于“值”而言。
显示情况下,将值从一种类型转换成另一种类型称之为类型转换,隐式情况下,称之为强制类型转换。类型转换发生在静态类型语言的编译阶段,强制类型转换则发生在动态类型语言的运行时。
关于语言的分类可以参见这篇文章:
一文理解静态语言、动态语言、解释型语言、编译型语言、强类型语言、弱类型语言 - 掘金 (juejin.cn)
JavaScript是一种弱类型,动态言语,所以我们讨论较多的也是强制类型转换。为方便区分又可以将强制类型转换分为 “显示强制类型转换” 和 “隐式强制类型转换”。
在介绍“显示”与“隐式”之前,就必须了解在JavaScript中的字符串、数字、布尔值之间的类型转换。因为这三者的转换在强制类型转换中扮演着非常重要的角色。 在ES5中就定义了一些类型的转换规则。与上述三者对应的分别是ToString、ToNumber、ToBoolean,附带会涉及一些ToPrimitive。
ToString
将非字符串到字符串的强制类型转换。
基本类型值的字符串化规则是:null==>"null";undefined==>"undefined";true==>"true".数字则遵循通用规则。对于普通对象而言,除非自定义tostring()否则其tostring()返回的都是内部属性[[Class]]的值,如"[[Object Object]]"
将对象强制类型转换为String是通过ToPrimitive抽象操作实现的。
在这里特别说一下在前端中的JSON字符串化
工具函数JSON.stringify()在将对象序列化为字符串时也用到了ToString,但是JSON的字符串化并非严格意义上的强制类型转换。
对于大多数简单值来说JSON的字符串化和toString()的效果基本相同,只不过序列化的结果总是字符串。如:
JSON.stringify(42);//"42"
JSON.stringify("42");//""42""
JSON.stringify(null);//"null"
JSON.stringify(true);//"true"
所有安全的JSON值都可以JSON.stringify()字符串化,那么不安全的JSON值怎么办呢?
所谓不安全的JSON值,主要包括:undefined、function、symbol和包含循环引用的对象。这些值不符合JSON的结构标准,所以支持JSON的语言无法处理它们。所以JSON.stringify()在遇到undefined,function,symbol的时候会自动将其忽略,在数组中返回null(以保证单元位置不变)。
JSON.stringify(
[1,undefined,function(){},4]
);//"[1,null,null,4]"
如果对象中定义了toJSON()方法,JSON字符串化时会优先调用该方法,然后用其返回值来进行序列化。如果要对含有非法JSON值的对象做字符串化,或者对象中的某些值无法被序列化的时候就需要自定义一个toJSON()来返回一个安全的JSON值。
var 0={};
var a={
b:42,
c:o,
d:function(){}
};
o.e=a;//在a中创建一个循环引用
var bb=JSON.stringify(a);
consloe.log(bb);
上面的这段代码会报循环引用错误
这个时候需要自定义一个toJSON()来返回安全的JSON值。
……
o.e=a;
a.toJSON=function(){
return {b:this.b};//序列化仅包含b
};
var cc=JSON.stringify(a);
console.log(cc)//"{"b":42}"
ToNumber
有时候我们需要将非数当作数字来使用,这个时候就会使用到ToNumber.
其中一些常见的转换有:true=>1;false=>0;undefined=>NaN;null=>0
ToBoolean
说道布尔值的转换,很多人首先会想到true为1,false为0,虽然它们之间是可以相互转换,但并不意味着两者是相等的。
在说到ToBoolean的时候就要介绍一下JavaScript中的 “假值”。
JavaScript中的值可以粗略的分为两类:
(1)可以被强制类型转换为true的值;
(2)可以被强制类型转换为false的值;
而在JavaScript中可以被强制类型转换为false的值是少数的:
- undefined
- null
- false
- +0,-0,NaN
- ""
按道理来说以上假值列表之外的值应该就是真值了,但是官方并不是那么明确的,JavaScript规范中只是说“所有的对象都是真值”。所以我们也可以理解成假值列表之外的都是真值。
那么介绍了那么多,终于要来到我们的“显示强制类型转换”和“隐式强制类型转换”了!
显示强制类型转换
对于显示强制类型转换而言是较为简明易懂的,其调用的转换方法或者函数都是显而易见的。所以简单点的带过。
字符串与数字的转换
字符串与数字的转换的转换主要是JavaScript的两个内建函数String(),Number();
var a=42;
var b=String(a);//b="42"
var c="42";
var d=Number(c);//d=42
//注意,它们前面是没有new关键字的,说明并不创建封装对象;
除了上面这种明显的方法之外,还有三种不那么常见的写法:
- “+”,"-" 看以下代码:
var c="42";
var d=+c;//d=42
可以看到“+c”的效果等同于Number(c);在这里“+”是一个一元运算符,会将操作数显示的转换为数字。如果想表达加一个操作数的话就在两个+之间空一格。
var c="42";
var d=5+ +c;//d=50
“-”与“+”的效果是一样的,并且“-”还会反转数字的符号位。
2."~"
字符操作“~”(非)准确来说其实不能完全算是强制类型转换,但是在某些方面能够实现强制类型转换的效果。
我们知道字位运算只适用于32位整数,运算符会强制操作数使用32位格式。首先会使用到ToInt32(ES5中的规范)执行ToNumber强制类型转换为数字,然后再将其转为32位整数的表达形式。
所以对于~而言就是先将值强制类型转换为32位数字,再执行字位操作“非”(对每一个字位进行反转,与隔壁的!很像)。
除此之外~~ 还可以用来截取小数部分,在~~ 中第一个~ 执行ToInt32并反转字位,第二个~再进行一次字位反转,如此得到的便还是ToInt32的结果。对于负数的处理类似于Math.floor().
Math.floor(-49.6);//-50
~~ -49.6;//-49
除此之外我们还可以使用“ +"" ”这种方式来将数字转换为字符串,原理其实很简单,我们知道在JavaScript中如果一个数字加上一个字符串,得到的结果就是一个字符串。+号不执行算术运算。
布尔值与数字的转换
在将某些复杂的布尔表达式转换为数字加法的时候,隐式强制类型转换就派上大用场了。 来对比一段代码:
function onlyOne(a,b,c){
return !!((a && !b && !c)||(!a && b && !c)||(!a && !b && c);
}
//但是如果有多个参数呢?还是这样些布尔表达式吗?
//当然不是!
function onlyOne(){
var sum=0;
for(var i=0;i<arguments.length;i++){
if(arguments[i]){
sum+=arguments[i];
}
}
return sum==1;
}
//判断的逻辑是,只要有一个参数为TRUE那么结果就为1,否则结果就不为1。
var a=true;
var b=false;
onlyOne(a,b,b);//true
onlyOne(b,a,b);//true
onlyOne(a,b,a);//false
布尔值的隐式强制类型转换
在程序中常见的隐式转换有:
(1)if()语句的条件表达式;
(2)for()循环的条件判断表达式;
(3)while(),do...while()中的条件判断表达式;
(4)? : 三元运算符的条件判断表达式;
(5)||,&& 的条件判断表达式;
可见关于布尔值的隐式强制类型转换都是一些条件判断表达式的结果。
插播一下||,&&
在JavaScript中的||,&&运算符与我们常规了解的还是有一些有趣的差别的。
在JavaScript中||和&&可以当选择器运算符使用!
因为在JavaScript中它们的返回值并不是一个布尔值而是两个操作数中的一个。
(1)对于||来说,如果条件判断结构为true,就会返回第一个操作数,如果为fasle就返回第二个操作数;
(2)对于&&来说,就刚好相反。
我认为了解JavaScript中的强制类型转换对代码的简洁性和精炼程度有很大的帮助,特别是一些运算符的使用,好好琢磨琢磨还是可以节省很多的代码量,同时还具有更好的安全性。
对于JavaScript中关于强制类型转换的内容就那么多了,如果有不足或者不准确的的地方欢迎大家评论区批评指正!我会及时更改的!