三种方法:
toString
、
String()
、
+ ''
总结来讲就是
null
、
undefined
、
true
、
false
都会转换成对应的原始字符串,数字的话,所有数字类型都会以原始值的形式转换为字符串,包括
NaN
、
Infinity
、
-Infinity
,但是对于
-0
的话,转换为字符串并不会保留它的符号,转换后值为
'0'
至于
Symbol
的话,如果你尝试将它转换成为一个字符串类型的话,那你将会得到一个类型报错:
var symbol = Symbol()
symbol += ''
这里看到,Symbol类型值是不允许类型转换的
对于普通的对象{}来讲,除非有自行定义的toString函数,否则toString返回的是内部属性[[Class]]的值,就像这样子的 '[object object]' ,对象类型在向字符串转换时,会自动调用其内的toString方法,如果找不到内部自己定义的toString方法,便会顺着原型链向上找,也就是说,在没有自己指定toString的时候,将一个对象转换成字符串的操作,调用的是Object.prototype.toString,这里看其实就特别熟悉了,这就是我们常常用来检测一个变量的真实类型的方法:
function checkType(value) {
return Object.prototype.toString.apply(value).split(' ')[1].split('').join('').replace(']', '').toLowerCase()
checkType(1)
checkType('')
checkType({})
checkType([])
下面是默认对象toString和自定义toString
对象默认toString
const obj = { name: 'young' }
console.log(obj.toString())
console.log(obj + 'Hello')
自定义toString
const obj = {
name: 'young',
toString: function () {
return JSON.stringify(this)
* 对象在转为字符串时自动调用其内的toString方法
console.log(obj + '')
数组这个特殊的对象类型它也定义了自己的toString:
[1, 2, {}].toString()
可以看到,数组的toString方法对数据内每一个单元调用其toString方法且以逗号进行分隔后返回
使用JSON字符串化
JSON.stringify() 常常用来进行对象的序列化
JSON.stringify() 对于基本类型值的效果和toString是相同的,但是某些结果可能会和直接的toString不相同,例如下面利用它去处理一个字符串类型的数据:
JSON.stringify('Hello World')
结果是一个带着双引号的字符串,可见,JSON.stringify()返回的结果总是字符串,即使原本就是字符串,它也会将这个字符串使用引号包起来再返回
JSON.stringify(NaN)
JSON.stringify(Infinity)
遇到数字类型且为NaN、Infinity,JSON.stringify()会将它们处理成'null'
安全的JSON值与不安全的JSON值
Number
String
Boolean
undefined
function
symbol
包含循环引用的对象
这里分别输出一下用 JSON.strinify() 处理这四个不安全的值会得出什么结果:
undefined
JSON.stringify(undefined)
处理undefined后得到的结果并不是一个字符串,还是undefined
function
JSON.stringify(function () {})
JSON.stringify(() => {})
处理函数得到的结果也是undefined,并不是预期的字符串
symbol
JSON.stringify(Symbol())
处理Symbol值得到的结果也是undefined,也不是预期的字符串
包含循环引用的对象
先提一嘴,啥是 包含循环引用的对象,就是“你中有我,我中有你”,无限循环,这种对象是展开不完的,具体形式长这个样子:
const article = {
title: '物种起源'
const author = {
name: 'Darwin'
现在指定文章所属为author,作者的作品为article
article.belong = author
author.work = article
接下来看看它变成什么样子了,拿author举例:
不难发现这个对象永远展开不完,这就是循环引用的对象,接下来使用JSON.stringify()来处理它试试:
JSON.stringify(author)
这里就报错了,所以它无法处理包含循环引用的对象
这几种危险类型值还有特殊的情况,它们不仅仅会被转为undefined,在数组和对象中,它们有不同的表现:
JSON.stringify([undefined, funtion () {}, Symbol()])
JSON.stringif({a: undefined, b: function () {}, c: Symbol()})
| 值 | 单独处理结果 | 数组中 | 对象中 |
|---|
| undefined | undefined | null | 丢失 |
| function | undefined | null | 丢失 |
| Symbol | undefined | null | 丢失 |
| 循环引用对象 | Error | Error | Error |
所以若对象包含这4种情况的话,最好不要用JSON.stringify()处理,否则无法出现预期的结果,影响程序的执行
JSON.stringify的中间层toJSON
如果对象中定义了toJSON方法,用JSON.stringify()处理这个对象时就会首先调用toJSON方法,用它的返回值来进行处理,这里的用处就是:
此方法定义了哪些值该被序列化
如果要对含有非法JSON值的对象来做字符串序列化,或是对象中某些值无法进行序列化,那么需要定义一个 toJSON 方法来返回安全的JSON值
就接着刚才的循环引用对象来继续,上面说到,我们因为循环引用而让JSON.stringify()方法报错了:
const article = {
title: '物种起源'
const author = {
name: 'Darwin'
article.belong = author
author.work = article
但是我实在是想处理它,怎么办呢,这里通过toJSON方法来中转它:
author.toJSON = function () {
return {
name: this.name
JSON.stringify(author)
结果并没有报错,我通过toJSON方法返回了一个对象,其内只包含了author的名字Darwin,类型为字符串,是一个安全的JSON值,所以就能够得到正确的处理结果
JSON.stringify的其它参数
在MDN能够看到,JSON.stringify()的参数不止一个
JSON.stringify(value[, replacer [, space]])
后两个参数replacer、space为可选参数,意义如下:
replacer
类型:Array | Function
含义:指定对象序列化过程中哪些属性应该被处理,哪些应该被排除掉
const person = {
name: 'young',
age: 21,
city: 'GuangZhou',
id: '123456'
JSON.stringify(person, ['name', 'age', 'city'])
JSON.stringify(person, function (key, value) {
if (key !== 'id') return value
两者得到了相同的处理结果,用数组格式的replacer时,传入的是一个字符串数组,每个元素为需要进行处理的属性名,如果不需要处理某个属性名,不写它就好 了;用函数格式的replacer时,这个函数接收两个参数:key和value,第一个参数key表示对象的属性名,第二个参数value表示对象的属性值,被序列化的值的每个属性都会经过此函数的转换和处理。
space
类型:Number | String
含义:指定输出的缩进格式
用法:在space为Number类型(正整数)时,它表示了每级缩进的字符数;在space为字符串类型时,表示每一级缩进使用的字符,最长支持10位
const tree = {
branch1: 12,
branch2: 10,
branch3: '4'
JSON.stringify(tree, null, 2)
JSON.stringify(tree, null, '**')
向数字转换
三种方式 - * / %、parseInt \ parseFloat、Number
| 值类型 | 转换后 |
|---|
| undefined | NaN |
| null | 0 |
| true | 1 |
| false | 0 |
''空字符串 | 0 |
| Symbol | TypeError |
Number
使用函数Number()来转换数字:如果Number中传递的值是一个字符串的的话,且这个字符串包含非数字字符,那么Number便会返回NaN
parseInt
与Number不同,parseInt的专精就是将字符串数据转换为数字。parseInt将第一个参数转换为字符串,对这个字符串进行解析,直到遇到第一个不能转换为数字的字符,返回一个整数或者NaN,如果第一个字符就无法转换为数字,那么程序将返回NaN。
但是+和-这两个字符虽然无法转换为数字,但是parseInt能够识别它们并作为返回结果的依据,仅限它们在第一个字符时。
parseInt('-123')
parseInt('+123')
还有一个字符无法转换为数字,' '空格,和+ -一样,它也必须在字符串的头部才可以被parseInt识别,parseInt会忽略掉开头的所有空格。
parseInt(' 123')
parseInt(string, radix)
第一个参数string为要处理的数据
第二个参数代表了按照几进制处理数据,也就是处理结果整数的基数,类型为整数,值为2到36
经常可以看到,parseInt处理的结果都是十进制的数,但是它并不是默认去以10为基数来处理数据的。如果处理字符串由'0x'或0X开头(不传 radix 的情况下),parseInt会猜想你的radix是16,接下来就会以16为基数来处理数据;如果字符串以0开头,我一开始以为它是会以8为基数来处理数据的,但是事实告诉我并不是这样:
parseInt('012')
并没有像十六进制数('0X11')那样猜想我们是想以8为基数来处理当前字符串,转而给了我们一个十进制的处理结果,所以这里可以先这样理解:如果没有传radix时,对于0X或0x开头的字符串,parseInt会将其作为16进制数来处理,其它情况,parseInt都会采用radix = 10的情况来处理字符串,最好还是在parseInt的时候由我们手动指定radix的值。
对于那些非字符串的数据,parseInt首先会将它们先字符串化,再去进行操作,
如parseInt(false),先将false转换为字符串为'false',接下来parseInt开始解析,遇到第一个字符,十进制数中并没有'f'这个字符,故无法转换为十进制数,返回NaN
而当我们指定了16为处理基数时(radix = 16)——parseInt(false, 16),那么接下来的操作就是:先将false转换为字符串'false',然后parseInt以16为基数开始计算,首先遇到f字符,🌍人都知道十六进制中是有f这个数的,所以,是一个正确可以转换成整数的值,存下来;再往下来一个,a,也是十六进制中的整数,符合;再往下看,l,这个就不是十六进制中的整数了,所以到这里没法找到更多的可转换的字符,就停下,最后得到的结果就是十六进制的fa,我们再算一下就好了,也就是15 * 16^1 + 10 * 16^0,结果为 250
其它类型转换数字
顶上那个表格只写了常见的Boolean类型,空字符串,undefined,null还有Symbol的一些转换结果,现在在这里来总结一下平时不常见且不常用的转换结果:
Object
对象在转换为数字的时候例如这种操作:
const person = { age: 20 }
person + 1
对象在转换为数字的时候,首先会检查对象的valueOf方法,若valueOf返回了基本类型值,则使用这个值作为后续转换为数字的基本类型值,如果它不是基本类型(object...),那就接着去检查该对象的toString方法,拿着toString的返回结果去做转换,如果,我说的是如果,toString也没有返回基本类型,那么就产生TypeError。
这里来证明一下valueOf和toString的先后问题:
const person = {
name: 'young',
valueOf: function () {
return 100
toString: function () {
return 200
person - 1
这里可以看到,用的值是valueOf返回的100,的确先于toString返回的200,接下来去掉valueOf,只写一个toString:
const person = {
name: 'young',
toString: function () {
return true
person - 1
这里JavaScript会首先找person的valueOf方法,因为我们并没有重写valueOf方法,所以调用的是Object.prototype.valueOf返回结果得到了:
name: 'young',
toString: function () {
return true
也就是这里的原始对象,但是它是个引用类型值,不属于基本类型,那么就接着往下找,toString返回了一个true,是基本类型值,所以将拿着这个ture进行数字转换,🌍人都知道,true对应的Number类型值为1,所以1 - 1的结果自然也就是0了
接下来看一眼当valueOf和toString都返回的是引用类型值时是如何报错的:
const person = {
name: 'young',
toString: function () {
return []
person - 1
报错如下:
Array
Array的行为跟上面的Object就很相像了,但是唯一不同的就是,Array的toString重写了,而Object的toString默认用的是Object.prototype.toString,下面看一下区别:
const arr = [1, 2, 3]
const obj = {}
parseInt(arr)
parseInt(obj)
这是因为Array的toString的处理逻辑是:将每个元素转换成字符型,并用逗号隔开,返回最后的结果,也就是说这里arr返回的是'1,2,3',parseInt读到了一个'1',读到逗号的时候读取结束,所以结果为1
而Object的默认toString返回的都是'[object object]',parseInt无法识别'[',故在第一位就结束解析,返回NaN
其它,Array和Object的行为是相同的
向布尔值转换
这个就可以说是重中之重了,但同时也是最简单的
需要着重记忆的就是,在JavaScript中,只有这些值转换为布尔类型后是false:
undefined
false
上面这7个值,无一例外全部都是基本类型值,并不包括包装对象包装后的值,如:
const num = new Number(0)
const bool = new Boolean(false)
const str = new String('')
!num
!bool
!str
取反后的结果都为假,所以它们的都是真值,true
除了上面列表的7个值之外,其它所有的值都为真值,也就是进行布尔转换后的结果为:true
只要记住这7个值,程序中的布尔转换基本就没有问题了
总结此篇意在提醒自己强制类型转换在JavaScript中的重要性,并且在我们编程时得到了某个结果并不是它原来的类型,我们也需要知道它为什么会这样变,否则,debug之路将异常艰辛,如有错误内容,烦请指出。