JavaScript 学习笔记 之 强制类型转换 (一)

值类型转换

JavaScript中的值类型转换分两种

类型转换(显式,发生在编译阶段)

		var a = 42;
		var b = String(a);
		console.log(typeof b); //string

强制类型转换(隐式,发生在运行阶段)

		var a = 42;
		var b = a + "";
		console.log(typeof b); //string

但是不管是哪一种,返回的都是标量基本类型值,不会返回函数和对象(因此我们之前说过的封装不是严格意义上的类型转换)

抽象值操作

1. ToString

基本类型值的字符串化的规则为:

  • null,undefined,true等基本类型值分别转换为他们各自对应的"null","undefined","true"等
  • 数字的字符串化遵循通用规则,但是极大极小的数字使用指数形式,如"1.07e21"等
  • 对于普通对象来说,除非自己定义,否则toString()(Object.prototype.toString())会返回内部属性[[class]]的值如"[object Object]"(函数返回的是类似于"function(){/../}"的字符串)
  • 数组的默认toString()方法经过了重新定义,所有单元字符串化后再用","连接,如[1,2,3]转换为"1,2,3"

toString()可以被显式调用,也可以在字符串化的时候自动调用

JSON字符串化

(JSON.stringify(..)并不是强制类型转换,只是许多规则与ToString相同)

JSON.stringify(..)将JSON对象序列化为字符串时的规则

  • 对于大多数简单值(数字,字符串,布尔值)来说效果和toString()相同,但是如果括号里输入的是带双引号的字符串,那么结果也是带双引号的字符串
  • undefined,function,symbol和包含循环引用(对象之间互相引用)的对象都不符合JSON的结构标准,因此JSON无法处理他们,遇到这类值的时候JSON.stringify()会忽略他们,在数组中则会返回null来保证单元位置不变(对包含循环引用的对象执行则会出错)

2. ToNumber

简单的转换

  • true转换为1
  • false转换为0
  • undefined转换为NaN
  • null转换为0
  • 0开头的十六进制按照十进制处理

在转换对象的时候抽象操作ToPrimitive会首先通过内部操作DefaultValue

检查是否有valueOf方法,如果有并返回一个基本类型值,如果没有则对toString的值进行转化

如果valueOf和toString都不返回基本类型值,则会产生TypeError错误

3. ToBoolean

JavaScript中的假值

  1. undefined
  2. null
  3. false
  4. +0,-0和NaN
  5. ""

遇到这些值时,强制类型转换会返回false,其他都为true

字符串和数字的显式转换

字符串和数字的转换通常通过String()和Number()进行(不带New,因此并不会创建封装对象)

		var a = 42;
		var b = String(a);
		console.log(typeof b); //string
		var a = "42";
		var b = Number(a);
		console.log(typeof b); //number

除了以上两种还有其他方式可以实现同样的效果

1. toString()

toString方法首先自动为42创建了一个封装对象然后再调用Object.prototype中的toString方法

		var a = 42;
		var b = a.toString();
		console.log(typeof b); //string

2. +运算符

本例中的+a是+的一元形式(使用-也可以进行转化,但是会改变值),使用这种形式转化的数值进行运算的时候记得带空格(如+ +a)以区分++运算符

		var a = "42";
		var b = +a;
		console.log(typeof b); //number

比较常见的用法有

		var time = new Date();
		console.log(time, typeof time); //Mon Sep 24 2018 15:42:41 GMT+0800 (中国标准时间) "object"
		console.log(+time, typeof + time); //1537775001102 "number"

本例中将date对象强制转换为数字,返回一个Unix时间戳,值为从1970年一月一日00:00:00 UTC到当前时间的毫秒数

由于使用构造函数时,构造函数无参数是可以省略()的,因此可能会遇到+new Date这种用法

3.~运算符

~运算符跟以上两种运算符稍微有些不同

~作为位运算符首先有一个特点就是只适用于32位整数

因此~会先执行一个抽象操作ToInt32

而ToInt32又会先执行ToNumber进行一个强制类型转换然后再执行ToInt32

		var a = "42";
		var b = ~a;
		console.log(typeof b, b); //number -43

~x大致等同于 -(x+1)

因此只有当x=-1的时候~x才会返回0(~是字位操作而不是数学运算,所以并不会返回-0)

-1是一个哨位值,被赋予了一个特殊含义,比如JavaScript中的indexOf(..)函数,返回-1表示不存在

但是直接使用>=0或者== -1进行判断的话存在一个抽象泄露的问题(暴露了底层的实现细节)

因此有个更好的方法是使用~运算符

~和indexOf(..)一起可以将结果强制类型转换为真/假值

当indexOf(..)返回-1的时候~运算符将-1转换为假值0,其他情况一律为真

比如 if(~a.indexOf("a")) 判断 a字符串中是否存在"a"这一个子字符串

~~进行字位截除

由于~字位运算符只适用于32位整数

因此可以是用来进行除去数字值的小数部分

第一个~执行ToInt32后进行字位反转,第二个~再次进行同样的步骤,因此结果其实是执行了ToInt32的原值

		var a = "42.23333";
		var b = ~~a;
		console.log(b); //42

但是需要注意,这种方法只适用于32位数,且不完全等同于Math.floor(..)(向下取整)

		var a = "-42.23333";
		var b = ~~a;
		console.log(b,Math.floor(a)); //-42 -43

事实上使用0|a(或运算符,对二进制进行或运算)也能达到一样的效果,而且看起来还更简便一些,因为|运算符的空操作(0|x)只进行了ToInt32操作

		var a = "-42.23333";
		var b = a | 0;
		console.log(b); //-42

但是如果考虑到优先级的问题的话,我还是更倾向于使用~~运算符

			~~20 / 10          //2
			20 | 0 / 10        //20
			(20 | 0) / 10      //2

 

显式解析数字字符串

		Number("42"),//42
		Number("42px"),//NaN
		parseInt("42"),//42
		parseInt("42px"),//42

解析字符串允许字符串中含有非数字字符,解析按照左到右,遇到非数字字符停止

转换不允许出现非数字字符,否则失败并返回NaN

parseInt()针对的是字符串值,如果传入非字符串,那么会被强制转换为字符串

在ES5之前的parseInt()有一个大坑

如果没有第二个参数来指定转换的基数,那么会根据字符串中的第一个字符来决定

如果第一个字符是x或者X则转换为16进制

如果第一个字符是0,则转换为八进制

将第二个参数设定为10则可以避免该问题

ES5以后默认转换为10进制,除非另外指定

解析非字符串

			parseInt(1 / 0, 19) //18

看到这个例子是不是觉得有点无法理解?

解析非字符串的时候,首先会把参数强制转换为字符串再解析

1/0的结果是Infinity,而JavaScript中所有的值都有一个默认的字符串形式,所以可以直接转为"Infinity"

再回到基数19,代表有效数字范围为0-9和a-i

这个时候解析到"I"的时候是没问题的,但是接下来的"n"不在有效数字范围内,就像上面遇到了"42px"中的"p"一样

解析停止,返回的是"I",也就是18

此外还有一些看起来奇怪但是没有问题的例子

		console.log(
			parseInt(0.000008), //0,来自于0.000008中的0
			parseInt(0.0000008), //8,来自于8e-7中的8
			parseInt(false, 16), //250,来自于false中的fa
			parseInt(parseInt, 16), //15,来自function..中的f
			parseInt("0x10"), //16,来自于0x10
			parseInt("103", 2), //2,来自于103中的10
		)

显示转换为布尔值

和上面的String()和Number()一样Boolean(..)(不带new)也是显式的ToBoolean强制类型转换,但是这种方法并不常用

与++转换number类型类似,!!同样也可以用来转换boolean类型

		console.log(
			!!"0", //true
			!![], //true
			!!{}, //true

			!!"", //false
			!!0, //false
			!!null, //false
			!!undefined //false
		)

在if(..)这样的布尔值上下文中,如果没有使用Boolean(..)和!!,就会自动进行ToBoolean转换

猜你喜欢

转载自blog.csdn.net/Aproducer/article/details/82817461