zgwyfz 2019-11-08
JavaScript作为一门弱类型语言,我们在每天的编写代码过程中,无时无刻不在应用着值类型转换,但是很多时候我们只是在单纯的写,并不曾停下脚步去探寻过值类型转换的内部转换规则,最近通过阅读你不知道的JavaScript中篇,对js的值类型转换进行了更加深入的学习,在此分享给大家参考学习。
将值从一种类型转换为另一种类型通常称为类型转换,主要发生在静态语言的编译阶段;强制类型转换则发生在动态语言的运行阶段;JavaScript作为一门典型的动态弱类型语言自然而然采用的是强制类型转换(即隐式强制类型转换和显式强制类型转换);在js的强制类型转换总是返回标量基本类型值,如字符串、布尔、数字,不会返回对象和函数
var a = 42; var b = a + '';//隐式强制类型转换 var c = String(a);//显式强制类型转化
在阅读后面的内容之前,我们首先要明白下面几个概念,以方便对后续内容的理解
基本类型的几乎所有方法并非来自本身,而是来自于封装对象的原型对象,例如下面例子
const a = 1.2; console.log(a.toFixed(0));//1
基本类型数字并不存在toFixed方法,只是在访问该方法时候,js自动封装基本类型为对应的封装对象,再去访问该封装对象的原型上对应的方法,等同于下面例子
const a = 1.2; console.log(new Number(a).__proto__.toFixed());//0
const a = 123; const _a = new Number(123); console.log(String(a), _a.toString());//'123' '123' const b = true; const _b = new Boolean(true); console.log(String(b), _b.toString());//'true' 'true'
const a = 1.07*1000*1000*1000*1000*1000*1000*1000; console.log(String(a));//'1.07e+21'
const b = {}; console.log(String(b));//[object object]
const a = [1, 2, 3]; console.log(String(a));//'1,2,3'
5.JSON字符串化
console.log(Number(null));//0 console.log(Number(undefined));//NaN console.log(Number(true));//1 console.log(Number(false));//0
console.log(Number('123'));//123 console.log(Number('0b111'));//7 console.log(Number('0o123'));//83 console.log(Number('0x123'));//291 console.log(Number('123a'));//NaN console.log(Number('a123'));//NaN
const arr = [1, 2, 3]; console.log(Number(arr));//NaN console.log(Number(arr.toString()));//NaN const num = new Number(123); console.log(Number(num));//123 console.log(Number(num.valueOf()));//123 const bool = new Boolean(true); console.log(bool.valueOf());//true console.log(Number(bool));//1 console.log(Number(bool.valueOf()));//1 const obj1 = { toString:()=>"21" } const obj2 = { valueOf:()=>"42", toString:()=>"21" } const obj3 = { a:1 } console.log(Number(obj1));//21 console.log(Number(obj2));//42 console.log(obj3.toString());//[object Object] console.log(Number(obj3));//NaN const obj = Object.create(null); console.log(Number(obj));//TypeError
上述obj1,obj2分别调用toString和valueOf方法,obj3调用原型上的toString()方法
位运算NOT的显式转换
const a = true; console.log(~a === -Number(a)-1)//true //indexOf优雅写法 const b = 'abc'; if(~b.indexOf('d')){ console.log('存在d') }else{ console.log('不存在d') } //数字的小数部分截除 const d = 3.14; const e = -3.14; console.log(Math.floor(d), ~~d);//3 3 console.log(Math.floor(e), ~~e);//-4 -3
解析字符串中的数字和强制将字符串转换为数字返回的结果都是数字;但是解析允许字符串中含有非数字,解析按从左到右的顺序,如果遇到非数字就停止解析;而转换不允许出现非数字字符,否则会失败并返回NaN。
注意点:es5之前parseInt()遇到0开头的字符串数字(比如时间hour='09')会被按照八进制处理,需要在第二个参数传入10解决,es5之后0开头的能能字符串化为八进制的按八进制解析不能的按10进制解析;
//现将a转为字符串'1,2,3' const a = [1, 2, 3]; console.log(parseInt(a));//1 console.log(parseFloat(a));//1 //现将true转为字符串'true' console.log(parseInt(true));//NaN console.log(parseFloat(true));//NaN //现将3.14转为字符串'3.14' console.log(parseInt(3.14));//3 console.log(parseFloat(3.14));//3.14 console.log(String(new Date()));//'Wed Jun 12 2019 21:23:59 GMT+0800' console.log(parseInt(new Date()));//NaN console.log(parseFloat(new Date()));//NaN //关于es6之前八进制写法的解析 console.log(parseInt(09));//9 console.log(parseFloat(09));//9 console.log(parseInt(010));//8 console.log(parseFloat(010));//8
parseInt(1/0, 19);//18 //其实相当于parseInt(Infinity, 19);其中Infinity的I在十九进制数中为18 parseInt(0.000008);//0 //字符串化为0.00008后进行解析 parseInt(0.0000008);//8 //字符化为8e-7后进行解析(详见抽象ToNumber) parseInt(0x10);//16 String(0x10);//16 parseInt(0b10);//2 String(0b10);//2 parseInt(0o10);//8 String(0o10);//8 parseInt(012);//10 String(012);//10 //其实现在es6规定了二进制、八进制和十六进制的表示法 //以上三个字符串均先被String()化为字符串再进行解析
const a = 1; console.log(a + true);//2 //等同于 console.log(a + Number(true));//2 console.log([1, 2] + [1, 2]); //1,21,2 //等同于 console.log([1, 2].toString() + [1, 2].toString()); //1,21,2 console.log({} + []);//[object Object] console.log([] + {});//[object Object] console.log({}.toString());//[object Object] console.log([].toString());//''
console.log('3.14' - '0');//3.14 console.log([2] - [1]);//1 //等同于 console.log([2].toString() - [1].toString());//1
以下均遵循ToBolean抽象值操作
Symbol不能被强制转换为数字(显式和隐式都会产生错误),但可以被强制转换为布尔值(显式和隐式结果都为true)
==允许在相等的比较中进行强制类型转换,===则不能允许,并不是==检查值是否相等,===检查值和类型是否相等
1.字符串和数字之间的相等比较
const [a, b] = ['42', 42]; console.log(a == b);//true //等同于 console.log(Number(a) === b);//true console.log(b == a);//true //等同于 console.log(Number(a) === b);//true
2.其他类型和布尔类型之间的比较
const [a, b] = [true, 1]; console.log(a == b);//true //等同于 console.log(Number(a) === b);//true console.log(b == a);//true //等同于 console.log(b === Number(a));//true const [c, d] = [false, 0]; console.log(c == d);//true //等同于 console.log(Number(c) === d);//true console.log(d == c);//true //等同于 console.log(d === Number(c));//true console.log('true' == true);//false //等同于 console.log('true' === 1);//false
3.null和undefined之间的相等比较,规范规定null和undefined宽松相等
console.log(null == undefined);//true
4.对象和非对象之间(包括数字、字符串;其中布尔遵循其他类型和布尔类型之间的比较)的相等比较
const [x, y] = [['42'], 42]; console.log(x == y);//true //等同于 console.log(x.toString() == y);//true const x1 = 'abc'; const y1 = new String(x1); console.log(x1 == y1);//true //等同于 console.log(x1 == y1.valueOf());//true
5.一些特殊情况
const [x, y, z] = [undefined, null, NaN]; console.log(x == Object(x) );//false console.log(y == Object(y) );//false //等同于 console.log(x == Object() );//false console.log(y == Object() );//false console.log(z == Object(z) );//false //等同于 console.log(z == new Number(z) );//false //由于Objec(undefined)和Object(null)没有对应的封装对象,所以不能够被封装, //Objec(undefined)和Object(null)均返回常规对象,等同于Object() //Object(NaN)等同于new Number(NaN), NaN==NaN返回false
6.假值的相等比较
null == '0';//false null == false;//false null == '';//false null == 0;//false undefined == '0';//false undefined == false;//false undefined == '';//false undefined == 0;//false null == undefined;//false //null只会与undefined宽松相等 '0' == false;//true ---特殊 '0' == NaN;//false '0' == 0;//true '0' == '';//false false == NaN;//false false == 0;//true false == '';//true ---特殊 false == [];//true ---特殊 false == {};//false '' == NaN;//false '' == 0;//true ---特殊 '' == [];//true '' == {};//false 0 == NaN;//false 0 == [];//true ---特殊 0 == {};//false 0 == '\n';//true ---特殊
7.抽象关系比较>、<、≥、≤
const a = [42]; const b = ['43']; console.log(a < b);//true //ToPrimite转换 console.log(a.toString() < b.toString()); //按照字母顺序判断 console.log('42' < '43');//true const a1 = ['42']; const a2 = ['043']; console.log(a1 > a2);//true
var a = { b: 42 }; var b = { b: 43 }; a < b; // false a == b; // false a > b; // false a <= b; // true a >= b; // true
按理两边对象都会进行ToPrimitive抽象值操作,转换为[object object]应该相等,但是结果却并非如此,具体原理参考ECMAScript5规范11.8节
8.原理巩固
const a = new Number('something'); let i = 2; Number.prototype.valueOf = ()=>i++; console.log(a == 2 && a == 3);//true
![]首先转换为false, [] == false符合上面的假值相等
[null]进行ToPrimitive强制类型转换为''
9.安全运用隐式强制类型转化
最后惯例,欢迎大家star我们的人人贷大前端团队博客以及个人github,所有的文章还会同步更新到知乎专栏 和 掘金账号,我们每周都会分享几篇高质量的大前端技术文章。如果你喜欢这篇文章,希望能动动小手给个赞。