跳到主要内容

ToString

四种通用方法

String()

一般来说,使用最安全的还是利用构造函数进行转换,可以避免因某个类型的值的toString()方法被重写造成的错误。

根据 ES 规范文档的描述,String()单独调用时执行的就是文档里描述的ToString这个抽象操作,可以认为是强制类型转换。

value.toString()

如果使用call传入某个对象来调用Object.prototype.toString并不会发生转换成字符串的操作,这是Object.prototype.toString机制导致的,所以几乎所有其他类型都在自己的原型链上重写了toString这个方法,除了Object,所以对象使用toString这个方法并非来自Object.prototype.toString而是他们自己的原型对象上的。

toString的局限是:

  • nullundefined无法使用toSring
  • 对普通对象使用toString只会返回[object, Object]
var obj = {
name: 'test',
};

console.log(obj.toString()); // [object, Object]
类型结果
UndefinedTypeError
NullTypeError
Boolean"true" / "false"
Symbol"Symbol([[Description]])" / "Symbol()"
ArraytoString 会调用 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

ESMA - Addition Operator

根据 ES 规范文档的描述,+连接两个值时,要么是进行字符串String连接,要么是数字Number相加,转换规则如下:

  • 首先使用ToPrimitive+两边值都转换成原始值

  • 然后只要一边有字符串类型,就都转成字符串,进行字符串连接操作。

  • 否则都转成 Number,然后相加

使用+的局限是:

  • 语法上看起来不太好懂,如果是 JS 基础不好的人可能不明白这是什么意思
  • Symbol类型的值不能直接使用+连接,必须先toString才行

JSON.stringify

JSON支持的类型有ObjectArrayNumberStringBooleanNull,对这些类型的对象进行序列化,可以将其转换成字符串形式,这个方法在转换字符串的时候如果不注意某些特殊值,就是有损转换;尤其 ES6 以后的MapSetSymbol完全不能用。

可以自己在类型的原型对象上定义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

array.join(separator)

根据 ES 规范文档的描述,join这个方法具有通用性,类数组对象也可以使用,其执行规则如下:、

  • 执行 ToObject,原始值类型会进行包装对象;
  • 获取数组或者类数字对象的length属性值;
  • 如果未指定分隔字符串,默认分割字符串seq是逗号",";否则seq就是ToString(separator)
  • 创建一个新的空字符串R
  • 循环获取数组元素,如果元素是undefinednull或者空值,看作空字符串;如果不是这三种情况,则进行*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.toStringFri Jul 24 2020 22:22:44 GMT+0800 (中国标准时间)
dateObj.toDateStringFri Jul 24 2020
dateObj.toTimeStringFri Jul 24 2020
dateObj.toISOString2020-07-24T14:22:44.600Z
dateObj.toUTCStringFri, 24 Jul 2020 14:22:44 GMT
dateObj.toJSON2020-07-24T14:22:44.600Z
dateObj.toLocaleDateString下午 10:22:44(浏览器语言设定是中文)
dateObj.toLocaleTimeString下午 10:22:44(浏览器语言设定是中文)
dateObj.toLocaleString2020/7/24 下午 10:22:44(浏览器语言设定是中文)

toString

Date.prototype.toString

也就是转换成时间形式的字符串,这个结果就是日常 Chrome 控制台显示的结果,它的规定形式如下:

  • week —— 表示 week 的单词取前三个字母
  • month —— 表示 month 的单词取前三个字母
  • day —— 2 位表示一个月中第几天的数字,单日前面要补 0
  • year —— 4 位年份数字,不够在前面补 0
  • hh小时
  • mm分钟 mm
  • ss
  • 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