理解js类型转换
在js中经常将类型转换分为显式和隐式的类型转换,其实这个是相对的,当了解了在某种数据的转换的时候他就是显式的,当你不知道的时候就是隐式的var a = 123 ,String(a) //这个大部分人都知道这个是对a的字符串转换,他就是显式的。而 a + ' ' 若这个你不知道的时候他就是隐式的 在进入今天的主题之前需要先了解一下几个函数
- toString()
- toNumber()
- toBoolean()
- ToPrimitive()
一.转化的基本函数
- toString()
- 基本类型 ,例如,null,undefined,true,都转成对应的字符产形式
- 数字(Number)
var a = 1.7 * 1000 * 1000* 1000 * 1000* 1000 * 1000 * 1000 a.toString() // '1.07e+21' 这个发生在数值比较大的时候,当数值比较小的时候返回的依旧是本身字符串,在chrom中的测试是这样
- 数组 (Array)
var a = [1, 2, 3] a.toString() // '1,2,3' [].toString() // '' //将对象转为字符串其实是调用ToPrimitive()方法
- JSON
json中也有JSON.stringify()方法将json对象转为字符串,其实也是调用了toString()方法
所有安全的值都可以被转化, 不安全的值有 (undefined, function,symbol,)这个几个值在调用JSON.stringify()的时候会被忽略,在数字中返回null,来保证下标的正确JSON.stringify( 42 ) // '42' JSON.stringify( '42' ) // " '42' " 这个需要注意一下 JSON.stringify( null ) // " null" 这个需要注意一下 JSON.stringify( true ) // "true" 这个需要注意一下
JSON.stringify( undefined ) // undefined JSON.stringify( function() {}) // undefined JSON.stringify( [1, undefined, function(){},4] ) // "[1, null, null, 4]" JSON.stringify( { a: 2, function () { }} ) // "{a: 2}" //如果循环引用的对象会出错的
- toNumber()
- 这个和toString()方法类似
true转为1,false转为0,undefined转为NaN,null转为0
在处理对象的时候这个又会调用ToPrimitive函数进行转换
var a = { valueOf: function() { return '42' } } var b = { toString: function() { return '42' } } var differences = { toString: function() { return '42+' }, valueOf: function() { return '42' } } var c = [4, 2] c.toString = function() { return this.join('') } Number( a ) //42 Number( b ) //42 Number( c ) //42 Number( differences ) //'42' Number( '' ) //0 Number( [] ) //0 Number( ['abc'] ) //NaN
- 这个和toString()方法类似
true转为1,false转为0,undefined转为NaN,null转为0
在处理对象的时候这个又会调用ToPrimitive函数进行转换
- toBoolean()
- 转为false 的中有 undefined,null,false,+0,-0,NaN,0 ,'',这几个值在进行布尔类型 的转换的时候会被转为false
- 其他的所有值都会被转为true
- 在js中1,0,常常被看作true和false,但其实这个发生了类型转换的结果,在对于[],{},是时候返回true,这个是因为空数组和空对象都是一个对象
- 在创建变量的时候可以使用字面量和构造函数,对于构造函数所船创建的变量是一个对象,所以在进行转换的时候会被转为true
var a = new String('') var b = new Number(0) var c = new Array() console.log(Boolean(a && b && c)) //true
- ToPrimitive()
- 对valueOf和toString的调用顺序是按照首选类型来进行调用的 ?? (这句话暂定 )??
- 对于ToPrimitive() 可能会有些陌生,对于这个函数他的作用就是在发生类型转化的时候,如果需要转换是对象的话 就调用这个函数
- 可以理解调用对象的valueOf()方法,若返回值不是一个基础类型,(例如字符或者数字)就会调用toString(),相同如果没有返回基础类型就会返回一个err,当然如果对其直接要求转换为string的时候先调用toString() 可以去好好了解一下
- juejin.cn/post/684490…
包装对象
-
包装对象 ?
- 包装对象简单来说是对数据暂时转换为对象形式的一种过程
var a = 'haha'
var b = new String(haha)
当你使用 a.indexOf('h')
的时候是可以成功的,但是其实在js内部中字符串类型并没有这个方法,而是在掉用的时候进了包装,将a包装成了一个字符串对象,就等同于b。但是在执行完之后这个对象是会被销毁的。
var a = 'ha'
String.prototype.double = function () {
return this.valueOf() + this.valueOf();
};
a.double() // 'haha'
这个例子说明了在过程中确实存在一个字符串到字符串对象包装的过程
- 关于更多的包装对象可以查看javascript.ruanyifeng.com/stdlib/wrap…
二.(显式)转化
-
string 与 number
- 在字符串和数字之间的转换是通过Number()和String() 来转化实现的
- Number()就是遵循toNumber()方法,String()遵循toString()方法这两个方法上面已经介绍过
- 当然你可能看过这样的代码
在这个代码中的 +b 就是对b的类型转换,其实内部是调用了toNumber()。下面对 + 运算符进行讨论var a = 1 var b = '2' a + +b //3
- 在 + 的转化下还可以实现日期对象Date的转化
当然也有getTime()和now()函数也可以直接获得时间戳,推荐使用now()。 在一个变量前使用 + 的时候这个时候是期望返回这个变量的数字类型var a = +new Date() console.log (a) //1604300514847
- 奇特的~运算符
- 这个运算符是按位取反(有兴趣的朋友可以去好好了解一下)
- ~运算符可以简单的理解为 ~a = - (a + 1)
- 利用这个特性可以在数组查询中利用indexOf()返回-1的特性来判断数组中有无某个元素
~还可以做字位截除(大家可以去了解一下)var a = [1, 2, 3] if(~a.indexOf(0)) console.log(0) else console.log(1) //这里打印的是0
- 字符转换为数字可以直接Number(value)但这种value不能夹杂其他的字符直接返回NaN ,这个时候来新型parseInt(value)来进行转化 ,value为一个字符串,如果不是一个字符按串就会发生不可预料的事情例如:
paserInt(1 / 0,19) // 18
你会好奇为什么返回了一个18,在这行代码中 1 / 0并不是一个字符串,而是一个表达式,计算过程就是 1 / 0 返回 Infinity 对这个字符串转化,I 为 18,n返回无 所以返回18 - 其实对于parseInt(value) 如果传进去的value不是一个字符串,会对value调用toString()方法
var a = { num: 1, toString: () => { return '21' } paserInt(a) // 21
- (显式)转为布尔值
- 和前面一样Boolean()就是调用toBoolean()
- 在这里主要讨论其他的转化、换为布尔值的方法 !,和前面的 + 运算符一样 可以显式的转为布尔值,当然会进行取反,所以可以使用 !!对变量进行显示的转为布尔类型。在if()中即使没有使用Boolean()或者 !!方法也会进行隐式转换,这个其实我们经常用到。
三.(隐式)转化
- 字符串和数字之间的隐式转换
var a = 1 var b = '2' a + b // '12'
- 在这个地方就是使用了数字到字符串的隐式转换,当然这个是很容易看出来的,你也可以说他是显式转换,这个完全取决自己。 看下面的例子
这个又怎样进行转化呢?在es5规范中如果某个操作数是字符串或者能通过一下步骤转为字符串的话,+将进行拼接操作。 如果一个操作数是对象,则首先对其调用toPrimitive()方法将其转为基本值类型所以上例子中结果为当加号两面不同时为数字且有一个为字符串的时候,期望两边转为字符串(个人理解), 所以 1 + true // 2 '1' + true //'1true' var a = [1, 2 , 3] var b = [3, 2 , 1] a + b // ???
1,2,33,2,1
- 在这里 提一个坑就是
[] + {} 和 {} + []
这个两者返回完全不一样的值,但是也涉及到了类型转换的问题 、 - 这个问题 + 两边调用toPrimitive(),先调用valueOf()返回本身不是基本类型,掉用toString() 返回 字符串 ,{}.toString() 返回的是 [Object, Object] , [].toString() 返回的是 ''
- 先说
[] + {}
的问题 它返回的是[object Object]
这个遵循上面所介绍的 - 对于
{} + []
它返回的是0,为什么是0呢?在js对代码进行解析的时候, 当{} 在前面的时候会被认为一个代码块 ,代码块中为空不执行, 然后是 + [] 对 [] 类型转化为 0 ----(这里在浏览器进行验证的时候如果使用console.log()直接进行打印的时候并不能得出0,这个是因为console.log()本身就存在隐式转换) - 当然还有
1 - '1'
这种情况,这个时候就会对'1'进行转换 - 就不讨论了
- 在这里 提一个坑就是
- 在这个地方就是使用了数字到字符串的隐式转换,当然这个是很容易看出来的,你也可以说他是显式转换,这个完全取决自己。 看下面的例子
- 布尔到数字的转化
- 这个没什么好说的true 转为 1 ,false转为 0 .
- 在一下中会发生到布尔值的隐式转化
if() ,while() do .. while(), for() 中的判断 ,? :三元判断 ,|| , &&
,以上会被隐式的转为布尔值,遵循上面的toBoolean()
- || 和 &&
- 在js中这两个运算符和其他语言的不太一样,他更像是一个选择器,在es6之前对函数的默认参数设定就是使用 || 运算符进行操作的。
这个也发生了类型转化为布尔型a || b //a为真的时候,直接返回a,否则返回b a && b //a为假时返回a,a为真返回b
- 在js中这两个运算符和其他语言的不太一样,他更像是一个选择器,在es6之前对函数的默认参数设定就是使用 || 运算符进行操作的。
- 最重要的 ==(宽松相等) === (严格相等)
- 两者都时判断两个值是否相等,大多数认为 == 只进行值判断 === 进行值判断和类型判断, 其实这个不是很正确的,正确是 == 允许进行类型转化而 === 不允许进行类型转化,所以其实在判断中 === 速度更快,当然很小很小,百万分之一吧
- == 转化规则
- 在 == 两边可能发生类型转化转化规则(个人总结)
x == y //一个为数字一个为字符串,字符串转数字 //若其中有一个为布尔类型,布尔转数字所以对于 2 == true 依然为false //若两者分别为null,undefined是返回true //若其中一个是对象,一个是基本类型,对对象调用toprimitive()在进行比较 // 总结: 总之在 == 两边期望转为数字进行比较 '0' == false // true 0 == false //true false == [] // 在这个中 false转为0 ,对于[] 调用toPrimitive(),在调用中期望转为数字 对象和数组都是valueOf()都是返回本身,所以再toString()对象返回 '[Object, Object]', 数组返回 '' 字符串,两者在转化为数字 对象为NaN,数组为0 '' == {} //false 这个和上面一样但是对一个队象调用toPrimitive(),在对象调用的时候valueOf()返回的是NaN, 原因如上
- 分析一下
[] == ![]
首先对![]进行转化,!存在隐式类型转化,[]转为布尔类型为ture,取反为false ,在返回数字为 0 ,左边相同的转化为 0 .。所以这个返回值应该是true - 在分析一下
2 == [2] 和 '' == [null]
同样都返回true 。原因也是一样的 [2] 转为 '2' 再转为 2 ,[null] 转为 '', 再转为 0 。。等式左边一样的道理。
- 此外涉及到对象之间的 == 就是判断引用地址是否为同一个这个就不再写了
- 参考文章
- 《你不知道的javascript》 javascript.ruanyifeng.com/stdlib/wrap…