ToString
四种通用方法
String()
一般来说,使用最安全的还是利用构造函数进行转换,可以避免因某个类型的值的toString()
方法被重写造成的错误。
根据 ES 规范文档的描述,String()
单独调用时执行的就是文档里描述的ToString
这个抽象操作,可以认为是强制类型转换。
value.toString()
如果使用call
传入某个对象来调用Object.prototype.toString
并不会发生转换成字符串的操作,这是Object.prototype.toString
机制导致的,所以几乎所有其他类型都在自己的原型链上重写了toString
这个方法,除了Object
,所以对象使用toString
这个方法并非来自Object.prototype.toString
而是他们自己的原型对象上的。
toString
的局限是:
null
和undefined
无法使用toSring
;- 对普通对象使用
toString
只会返回[object, Object]
var obj = {
name: 'test',
};
console.log(obj.toString()); // [object, Object]
类型 | 结果 |
---|---|
Undefined | TypeError |
Null | TypeError |
Boolean | "true" / "false" |
Symbol | "Symbol([[Description]])" / "Symbol()" |
Array | toString 会调用 Array.prototype.join,将每个元素转成 String,然后用逗号连接 |
Date | "Fri Jul 24 2020 01:00:27 GMT+0800 (Hong Kong Standard Time)" |
Function | 返回函数定义的整个源码字符串 |
普通对象 {} | "[object Object]" |
Number | 见下文 |
BigInt | 和 Number 表现基本一致 |
‘’ + xxx
根据 ES 规范文档的描述,+
连接两个值时,要么是进行字符串String
连接,要么是数字Number
相加,转换规则如下:
首先使用
ToPrimitive
将+
两边值都转换成原始值然后只要一边有字符串类型,就都转成字符串,进行字符串连接操作。
否则都转成 Number,然后相加
使用+
的局限是:
- 语法上看起来不太好懂,如果是 JS 基础不好的人可能不明白这是什么意思
Symbol
类型的值不能直接使用+
连接,必须先toString
才行
JSON.stringify
JSON
支持的类型有Object
,Array
,Number
,String
,Boolean
和Null
,对这些类型的对象进行序列化,可以将其转换成字符串形式,这个方法在转换字符串的时候如果不注意某些特殊值,就是有损转换;尤其 ES6 以后的Map
,Set
,Symbol
完全不能用。
可以自己在类型的原型对象上定义toJSON
方法,这样JSON.stringify
会在转换的时候调用该方法,从而弥补部分类型不支持的缺陷。
BigInt.prototype.toJSON = function() {
return this.toString();
};
JSON.stringify(BigInt(1));
// '"1"'
其它类型的方法
Number
Number.prototype.toString(radix)
@param radix 允许
[2, 36]
之间的整数,默认是10
;不在这个区间内的话,会抛出RangeError
首先要注意不能直接对一个数字使用toString()
方法,这种形式会在 JS 的词法分析阶段就报错(某些 IDE 也会提示),因为 JS 允许小数形式的字面量,如果在数字后面加上一点.
,那么会和前面的数字放在一起进行词法解析,最好用括号括起来表示数字直接量。
12.toString(); // SyntaxError
(12).toString();
根据 ES 规范文档的描述,总体来说,当一个Number
类型的数字调用toString
时,会根据传入基数radix
将数值先转成该基数,然后再做ToString
操作转换成字符串,规范写的很复杂,现在没看懂,不过有几个情况如下:
如果没传 radix,默认是以十进制为基数
如果是
NaN
,返回"NaN",如果是正负 0,都返回"0"
如果是正无穷,返回"Infinity"
如果带有负号,对数字部分转换后再在前面加上负号
对于一些有明显标志的十六进制
0x
,二进制0b
,八进制0o
这些要特别注意toString
的基数是什么;如果是十六进制的基数,结果可能还会出现[a, f]
之间的小写字母,代表[10, 15]
。
let x = 6;
console.log(x.toString(2)); // '110'
console.log((254).toString(16)); // 'fe'
console.log((-10).toString(2)); // '-1010'
console.log((-0xff).toString(2)); // '-11111111'
Symbol
Symbol.prototype.toString
Symbol
类型的对象也拥有自己原型对象上的toString()
方法,Symbol
类型的对象在创建的时候会关联一个私有属性[[Description]]
,也就是调用Symbol(description)
构造函数时传进去的字符串值,如果不传则[[Description]]
就是undefined
。
根据 ES 规范文档的描述,Symbol
类型对象在执行 toString 时,会返回字符串组合Symbol([[Description]])
,如果[[Description]]
是undefined
,那么只会返回Symbol()
console.log(Symbol('desc').toString()); // "Symbol(desc)"
console.log(Symbol().toString()); // "Symbol()"
Array
Array.prototype.toString
根据 ES 规范内容,toString
内部实际上会调用Array.prototype.join()
这个方法,如果这个方法不可被调用,才会使用继承的Object.prototype.toString
。一般来说都是执行join
,默认分隔符是逗号,
。
const elements = ['Fire', 'Air', 'Water', undefined];
console.log(elements.join()); // "Fire,Air,Water,"
console.log(elements.toString()); // "Fire,Air,Water,"
Array.prototype.join
根据 ES 规范文档的描述,join
这个方法具有通用性,类数组对象也可以使用,其执行规则如下:、
- 执行 ToObject,原始值类型会进行包装对象;
- 获取数组或者类数字对象的
length
属性值; - 如果未指定分隔字符串,默认分割字符串seq是逗号
","
;否则seq就是ToString(separator) - 创建一个新的空字符串R;
- 循环获取数组元素,如果元素是
undefined
,null
或者空值,看作空字符串;如果不是这三种情况,则进行*ToString转换 - 使用分隔符将每个字符串元素连接起来作为最后结果返回
const elements = ['Fire', 'Air', 'Water', undefined, null, ,];
console.log(elements.join()); // "Fire,Air,Water,,,"
console.log(elements.toString()); // "Fire,Air,Water,,,"
Array.prototype.reduce
实际上还可以使用reduce
将数组转换成字符串,而且这个方法是完全可控的。
[0, 1, 2, 3, 4].reduce(function(
accumulator,
currentValue,
currentIndex,
array,
) {
return accumulator + currentValue;
},
''); // "01234"
Date
方法 | 转换结果 |
---|---|
Date(str) | 当前时间字符串,Fri Jul 24 2020 22:22:44 GMT+0800 (中国标准时间) |
dateObj.toString | Fri Jul 24 2020 22:22:44 GMT+0800 (中国标准时间) |
dateObj.toDateString | Fri Jul 24 2020 |
dateObj.toTimeString | Fri Jul 24 2020 |
dateObj.toISOString | 2020-07-24T14:22:44.600Z |
dateObj.toUTCString | Fri, 24 Jul 2020 14:22:44 GMT |
dateObj.toJSON | 2020-07-24T14:22:44.600Z |
dateObj.toLocaleDateString | 下午 10:22:44(浏览器语言设定是中文) |
dateObj.toLocaleTimeString | 下午 10:22:44(浏览器语言设定是中文) |
dateObj.toLocaleString | 2020/7/24 下午 10:22:44(浏览器语言设定是中文) |
toString
Date.prototype.toString
也就是转换成时间形式的字符串,这个结果就是日常 Chrome 控制台显示的结果,它的规定形式如下:
week
—— 表示 week 的单词取前三个字母month
—— 表示 month 的单词取前三个字母day
—— 2 位表示一个月中第几天的数字,单日前面要补 0year
—— 4 位年份数字,不够在前面补 0hh
小时mm
分钟 mmss
秒GMT±[hh][mm]
—— 时区偏移量,只有这两个之间没有空格- 时区名称
var x = new Date();
var myVar = x.toString(); // Fri Jul 24 2020 01:00:27 GMT+0800 (Hong Kong Standard Time)
toDateString
dateObj.toDateString()
返回Date
类型对象的日期部分,由周,日,月,年依次排列组合,形如"Thu Jan 01 1970"
,相当于取toString
结果的日期部分。
返回的字符串固定长度是15
,表示形式有如下规定:
- 表示 week 的单词取前三个字母
- 表示月份的单词取前三个字母
- 表示日期的两个数字,必要时会补 0
- 表示年份的四个数字,必要时会补 0
const event = new Date(1993, 6, 5, 14, 39, 7);
console.log(event.toDateString()); // Mon Jul 05 1993
toTimeString
dateObj.toTimeString()
返回Date
类型对象的时间部分,由时,分,秒,GMT 偏移量,时区名称组成,相当于取toString
结果的时间部分。
var d = new Date();
console.log(d.toString()); // Fri Jul 24 2020 21:56:07 GMT+0800 (Hong Kong Standard Time)
console.log(d.toTimeString()); // 21:56:07 GMT+0800 (Hong Kong Standard Time)
toISOString
dateObj.toISOString()
这个方法返回相对于dateObj
时刻的 UTC 标准时间的字符串格式,形如YYYY-MM-DDTHH:mm:ss.sssZ
var d = new Date();
console.log(d.toString()); //Fri Jul 24 2020 22:11:43 GMT+0800 (Hong Kong Standard Time)
console.log(d.toISOString()); // 2020-07-24T14:11:43.204Z
toUTCString
dateObj.toUTCString()
这个方法返回相对于dateObj
时刻的 GMT 标准时间形式的字符串,也就是toString()
时间部分±
偏移量得到的时间;这个结果没有表示毫秒的部分,不看这部分的话,和上面的toISOString
得到的时间差不多。
var d = new Date();
console.log(d.toString()); // Fri Jul 24 2020 22:11:43 GMT+0800 (Hong Kong Standard Time)
console.log(d.toUTCString()); // Fri, 24 Jul 2020 14:11:43 GMT
toJSON
dateObj.toJSON()
根据 ES 规范文档描述,toJSON
方法最后会调用toISOString
进行转换,所以这个方法结果和toISOString
基本一样,当超出时间范围允许的最大值的时候返回null
console.log(new Date().toISOString()); // 2020-07-24T13:05:26.793Z
toLocaleDateString
dateObj.toLocaleDateString(locales, options)
@param locales 语言标记字符串(language code)
@param options 一个对象,包含年月日转换格式的属性
Locale
( /ləˈkɑːl/ )是本地的意思,这个方法是以指定语言编码的格式返回Date
类型对象的日期部分。如果不指定第一个参数语言标记,默认是使用浏览器的默认语言环境,如果修改浏览器的语言选项,那么默认结果也会发生变化。
这个方法的参数和Intl.DateTimeFormat
参数一样,第二个参数options
是一个对象,有很多复杂的属性,见——Intl.DateTimeFormat() constructor
// 在英文版的firefox中以en-US作为默认语言标记,返回美国的时间表示形式
console.log(new Date().toLocaleDateString()); // 7/24/2020
console.log(new Date().toLocaleDateString('en-US')); // 7/24/2020
// 而在中文版的浏览器中,则以zh-CN作为语言标记,返回中文的表示
console.log(new Date().toLocaleDateString()); // 2020/7/24
console.log(new Date().toLocaleDateString('zh-CN')); // 2020/7/24
toLocaleTimeString
dateObj.toLocaleTimeString(locales, options)
按语言转换Date
类型对象的时间部分,默认仍然是看浏览器的语言版本。
console.log(new Date().toLocaleTimeString('zh-CN')); // 下午9:35:12
console.log(new Date().toLocaleTimeString('en-US')); // 9:35:12 PM
toLocaleString
dateObj.toLocaleString(locales, options)
// 在设置English语言选项时候得到的转换结果
console.log(new Date().toLocaleString()); // 7/24/2020, 9:49:54 PM
console.log(new Date().toLocaleString('en-US')); // 7/24/2020, 9:49:54 PM
console.log(new Date().toLocaleString('zh-CN')); // 2020/7/24 下午9:49:54
Symbol.toPrimitive
dateObj.[Symbol.toPrimitive](hint)
@param hint 必传且只能是三者其中之一:"string" / "number" / "default"
根据传入的类型参数,将Date
类型的对象转换成String
或者Number
。根据 ES 规范文档的描述,引用类型的值转成基本类型值的时候执行的是ToPrimitive
操作,这个方法是抽象的,属于 JS 引擎层面的执行动作,但是Date
类型重写了这个方法,具有以下实现规则:
如果hint
的值是default
或者string
,这个方法会先尝试调用toString
并返回结果;如果toString
不存在,会尝试调用valueOf
并返回结果;如果还不存在,则TypeError
。
如果hint
的值是number
,先尝试调用valueOf
并返回结果;如果valueOf
不存在,会尝试调用toString
并返回结果;如果还不存在,则TypeError
。
如果不传hint
会抛出TypeError
。
console.log(new Date()[Symbol.toPrimitive]('default'));
// Sat Jul 25 2020 01:18:18 GMT+0800 (Hong Kong Standard Time)
console.log(new Date()[Symbol.toPrimitive]('string'));
// Sat Jul 25 2020 01:18:18 GMT+0800 (Hong Kong Standard Time)
console.log(new Date()[Symbol.toPrimitive]('number'));
// 1595610860808