前言JavaScript是一门弱类型语言。在每天写代码的过程中,我们无时无刻不在应用值类型转换,但大多数时候我们只是马不停蹄地在编写中探索值类型转换的内部转换规则。最近通过阅读你所不知道的JavaScript媒介,更加深入的研究了js的值类型转换。在此分享给大家参考学习。将值从一种类型转换为另一种类型通常称为类型转换,主要发生在静态语言的编译阶段;强制类型转换发生在动态语言的运行时阶段;JavaScript是典型的动态弱类型语言,自然而然会使用强制类型转换(即隐式强制类型转换和显式类型转换);js中的强制类型转换总是返回标量基本类型值,如字符串、布尔、数字,不返回对象和函数vara=42;varb=a+'';//隐式类型转换varc=String(a);//显式类型转换前的前提在阅读下面的内容之前,我们首先要了解以下几个概念,以方便理解后续内容封装对象:eg:vara=newString('abc'),a称为封装了基本类型的封装对象,要恢复一个封装对象的值,可以调用valueOf方法;几乎所有基本类型的方法都不是来自于自身,而是来自于被封装对象的原型对象,比如下面的例子consta=1.2;console.log(a.toFixed(0));//1个基本类型号不存在toFixed方法,但是在访问该方法时,js自动将基本类型封装为对应的封装对象,然后访问对应的方法上封装对象的原型,相当于下面的例子consta=1.2;console.log(newNumber(a).__proto__.toFixed());//0ToPrimitive抽象操作:这个操作主要是将对象类型转换为基本类型,首先检查一个对象是否有valueOf属性,如果有,返回对象的valueOf值,否则调用对象的toString属性并返回值(如果valueOf返回的不是基本类型,则调用toString方法,例如数组的valueOf返回数组,则所有ToPrimitives都会默认调用toString方法);抽象值操作ToString负责处理非string到characterString的强制类型转换,规则如下:1.null转换为'null',undefined转换为'undefined',其他基本类型调用basic类型的包装对象属性toString()并返回值。consta=123;const_a=newNumber(123);console.log(String(a),_a.toString());//'123''123'constb=true;const_b=newBoolean(true);console.log(String(b),_b.toString());//'true''true'2.数字的字符串化遵循一般规则,但是极小极大的数字使用指数形式consta=1.07*1000*1000*1000*1000*1000*1000*1000;console.log(String(a));//'1.07e+21'3.对于普通对象,toString()返回,除非你自己定义Object.prototype.toString()的值,其他对象有自己的toString()方法,然后调用自己的方法constb={};console.log(String(b));//[objectobject]4.Array重新定义默认的toString()方法,将所有单元字符串化后用','连接consta=[1,2,3];console.log(String(a));//'1,2,3'5.JSON字符串化5-1.JSON字符串化和toString的效果基本一样,只是序列化的结果永远是一个字符串5-2.JSON对于不安全值(undefined,function(){},symbol)直接忽略,数组用null5-3填充。对象循环引用直接报错,正则表达式序列化为{}ToNumber,负责非数字到数字的强制类型转换。规则如下:1.true转为1,false转为0,undefined转为NaN,null转为0console.log(Number(null));//0console.log(Number(undefined));//NaNconsole.log(Number(true));//1console.log(Number(false));//02.字符串的处理遵循数值常量的相关规定/语法,返回NaNconsole.log(Number('123'));//123console.log(Number('0b111'));//7console.log(Number('0o123'));//83console.log(Number('0x123'));//291console.log(Number('123a'));//NaNconsole.log(Number('a123'));//NaN3.对象(包括数组)首先会按照ToPrimitive抽象操作转换成对应的基本类型值,然后按照前两条规则进行处理;如果一个对象没有valueOf方法或者toString方法,就会产生TypeError错误(比如Object.create(null)没有上面两个方法)constarr=[1,2,3];console.log(Number(arr));//NaNconsole.log(Number(arr.toString()));//NaNconstnum=newNumber(123);console.log(Number(num));//123console.log(Number(num.valueOf()));//123constbool=newBoolean(true);console.log(bool.valueOf());//trueconsole.log(Number(bool));//1console.log(Number(bool.valueOf()));//1constobj1={toString:()=>"21"}constobj2={valueOf:()=>"42",toString:()=>"21"}constobj3={a:1}console.log(Number(obj1));//21console.log(Number(obj2));//42console.log(obj3.toString());//[object对象]控制台。log(Number(obj3));//NaNconstobj=Object.create(null);console.log(Number(obj));//上面的TypeErrorobj1,obj2分别调用toString和valueOf方法,obj3调用原型上的toString()方法。ToBoolean负责处理从非布尔值到布尔值的强制类型转换。规则如下:除了条件1外,可以转换为false的值(undefined,null,false,+0,-0andNaN,'')都转换为true(记住:所有封装对象都转换为true)通过window()下的内置函数String和Number()来实现字符串与数字的显式强制转换(按照上面抽象值操作)toString()的显式转换过程是先隐式转换基本类型封装成一个对象,然后在对象上调用toString方法一元运算符+和-转换,比如+a显式转换c为数字显式转换位运算NOT第1步:位运算NOT将非数和a转换number转换为32位数字第二步:对数字求反并减去1indexOf优雅的写法(返回-1的现象称为抽象泄漏,即代码暴露了底层实现细节)截断值小数点的数字是比Math.floor()更靠谱(Math.floor截取负数和~不同)consta=true;console.log(~a===-Number(a)-1)//true//indexOf写法优雅constb='abc';if(~b.indexOf('d')){console.log('existsd')}else{console.log('doesnotexistd')}//截去数字的小数部分constd=3.14;conste=-3.14;console.log(Math.floor(d),~~d);//33console.log(Math.floor(e),~~e);//-4-3显式解析数字字符串返回的结果将字符串转换为数字是数字;但是解析允许字符串包含非数字,并且解析是从左到右的顺序。如果遇到非数字,解析将停止;转换不允许非数字字符,否则将失败并返回NaNparseInt()和parseFloat分别用于解析整数和浮点数。传入值必须是字符串。如果是非字符串,会隐式转换成字符串再解析。注意:在es5之前,parseInt()遇到以0开头的字符串数字的值(比如timehour='09')会按八进制处理,需要在第二个参数中传入10来解决。es5之后,可以将0开头的字符串转为八进制。系统分析;//现在将a转换为字符串'1,2,3'consta=[1,2,3];console.log(parseInt(a));//1console.log(parseFloat(a));//1//将true转换为字符串'true'console.log(parseInt(true));//NaNconsole.log(parseFloat(true));//NaN//将3.14转换为字符串'3.14'console.log(parseInt(3.14));//3console.log(parseFloat(3.14));//3.14console.log(String(newDate()));//'WedJun12201921:23:59GMT+0800'console.log(parseInt(newDate()));//NaNconsole.log(parseFloat(newDate()));//NaN//es6console之前的八进制表示法解析。log(parseInt(09));//9console.log(parseFloat(09));//9console.log(parseInt(010));//8console.log(parseFloat(010));//8parseInt()一些奇怪的现象parseInt(1/0,19);//18//其实等价于parseInt(Infinity,19);其中Infinity的I为18parseInt(0.000008);//0//十六进制字符序列化后解析为0.00008parseInt(0.0000008);//8//特征化为8e-7后解析(详见摘要ToNumber)parseInt(0x10);//16String(0x10);//16parseInt(0b10);//2String(0b10);//2parseInt(0o10);//8String(0o10);//8parseInt(012);//10String(012);//10//其实es6规定了二进制,八进制和十六进制表示法//以上三个字符串都是先通过String()转换成字符串,然后解析显式转换成布尔值。全局方法Boolean()强制转换遵循抽象值操作ToBolean!!对于强制转换,在抽象值操作中遵循ToBolean隐式强制类型转换隐式强制类型转换为字符串一元运算符加号(+)首先通过ToPrimitive抽象操作将非基本类型转换为基本类型,如果其中之一加号中的两项是字符串,另一项是字符串拼接的ToString操作。如果是布尔值加数字,则对布尔值进行ToNumber运算,然后求和consta=1;console.log(a+true);//2//相当于console.log(a+Number(true));//2console.log([1,2]+[1,2]);//1,21,2//相当于console.log([1,2].toString()+[1,2].toString());//1,21,2console.log({}+[]);//[对象对象]console.log([]+{});//[对象对象]console.log({}.toString());//[objectObject]console.log([].toString());//''隐式转换为数字,通过一元运算符-、/、*转换,遵循ToNumber的抽象值运算规则console.log('3.14'-'0');//3.14console.log([2]-[1]);//1//相当于console.log([2].toString()-[1].toString());//1隐式强制转换为布尔值遵循ToBolean抽象值运算的if(..)语句中的条件判断表达式for(..;..;..)statementwhile(..)anddo..while(..)Theconditionaljudgmentexpressionof?:条件判断表达式逻辑运算符||和&&左边的操作数(a||b等价于a?a:b,a&&b等价于a?b:a),但可以强制转换为布尔值(显式和隐式结果都为真)松散相等和严格相等==允许在相等比较中进行强制转换,===不允许,不==检查值是否相等,===检查对于值和类型相等严格相等的两个特例NaN不等于NaN;+0等于-0;松等式之间的隐式转换1.字符串和数字的相等性比较(1)如果Type(x)是一个数字,Type(y)是一个字符串,则返回x==ToNumber(y)的结果(2)如果Type(x)是一个字符串,Type(y)是一个数字,返回ToNumber(x)==y的结果。const[a,b]=['42',42];console.log(a==b);//true//等价于console.log(Number(a)===b);//trueconsole.log(b==a);//true//等同于console.log(Number(a)===b);//true2。其他类型与Boolean类型的比较(1)ifType(x)如果是Boolean类型,则返回ToNumber(x)==y的结果。(2)如果Type(y)为Boolean,则返回x==ToNumber(y)的结果。const[a,b]=[真,1];console.log(a==b);//true//相当于console.log(Number(a)===b);//trueconsole.log(b==a);//true//相当于console.log(b===Number(a));//trueconst[c,d]=[false,0];console.log(c==d);//true//相当于console.log(Number(c)===d);//trueconsole.log(d==c);//true//相当于console.log(d===Number(c));//trueconsole.log('true'==true);//false//等同于console.log('true'===1);//false3.null和undefined相等比较,规范规定null和undefined松散相等console.log(null==undefined);//true4.对象和非对象之间(包括数字和字符串;其中Boolean跟在其他类型和Boolean类型之间比较)如果Type(x)是字符串或数字,Type(y)是对象,则返回x==的结果ToPromitive(y);如果Type(x)是一个对象并且Type(y)是一个字符串或数字,则返回ToPromitive(x)==y的结果;const[x,y]=[['42'],42];console.log(x==y);//true//等同于console.log(x.toString()==y);//trueconstx1='abc';consty1=newString(x1);console.log(x1==y1);//true//等同于console.log(x1==y1.valueOf());//true5.一些特殊情况const[x,y,z]=[undefined,null,NaN];console.log(x==Object(x));//falseconsole.log(y==Object(y));//false//相当于console.log(x==Object());//falseconsole.log(y==Object());//falseconsole.log(z==Object(z));//false//相当于console.log(z==newNumber(z));//false//因为Objec(undefined)和Object(null)没有对应的封装对象,所以不能封装,//Objec(undefined)andObject(null)两者都返回正则对象,相当于Object()//Object(NaN)相当于newNumber(NaN),NaN==NaN返回false6。假值的相等比较null=='0';//falsenull==false;//falsenull=='';//falsenull==0;//falseundefined=='0';//falseundefined==false;//falseundefined=='';//falseundefined==0;//falsenull==undefined;//false//null只会松散地等于undefined'0'==false;//true---special'0'==NaN;//false'0'==0;//true'0'=='';//falsefalse==NaN;//falsefalse==0;//truefalse=='';//true---specialfalse==[];//true---specialfalse=={};//false''==NaN;//false''==0;//true---special''==[];//true''=={};//false0==NaN;//假0==[];//true---特殊0=={};//假0=='\n';//true---特殊7.抽象关系比较>,<,≥,≤如果两边都是字符串,则按字母顺序比较。如果两边都是其他情况,先调用ToPrimitive转换为基本类型。如果转换结果不是字符串,则按照ToNumber规则强制转换为数字进行比较。常量a=[42];constb=['43'];console.log(aa2);//true对象关系比较的奇怪之处vara={b:42};变量b={b:43};一个<乙;//falsea==b;//错误>b;//falsea<=b;//真a>=b;//是的,从逻辑上讲,两个对象都会进行ToPrimitive抽象值操作,转换为[objectobject]应该是相等的,但结果却不是这样。具体原理可以参考ECMAScript5规范的Section11.8。8.a==2&&a==3怎么写?consta=newNumber('something');leti=2;Number.prototype.valueOf=()=>i++;console.log(a==2&&a==3);//true[]==![]为什么是真的?![]先转为false,[]==false等于上面的false值''==[null]为什么是true呢?[null]对''进行ToPrimitive强制转换9.安全使用隐式强制转换如果两边的值中有true或者false,不要使用==如果有[],''或者0两边的值,尽量不要用==,通过typeof判断最后的做法最安全。欢迎star我们的人人贷前端团队博客和个人github。所有文章将同步更新至知乎专栏和掘金号。每周都会分享几篇优质的前端技术文章。如果您喜欢这篇文章,希望您能给个赞。
