Skip to content

千位分隔符的思考

英语里没有 亿 ,只有 百万(million) 十亿(billion) ,千位分隔符就是这么产生的

1, 000 one thousand 一千

1, 000, 000 one million 一百万

1, 000, 000, 000 one billion 十亿

方案一: 正则

思路: 从个位往左数起,每三位前插入一个千位分隔符 , ,排除最开始的位置

String(Number).replace(/(\d)(?=(\d{3})+$)/g, "$1,");

js
String(1234567890).replace(/(\d)(?=(\d{3})+$)/g, "$1,");
// "1,234,567,890"

说明:

  • g是表示全局匹配的修饰符,全局匹配指查找所有匹配而非在找到第一个匹配后停止。
  • $是表示结尾的量词,如n$,匹配的是任何以n为结尾的字符串。
  • \d是查找数字的元字符。
  • n{X}是匹配包含 X 个 n 的序列的字符串的量词。
  • + 匹配前面的子表达式一次或多次; * 匹配前面的子表达式0次或多次。?匹配前面的子表达式0次或1次,或指明一个非贪婪限定符。
  • x(?=y)先行断言,匹配'x'仅仅当'x'后面跟着'y'
  • match() String对象的方法,作用是找到一个或多个正则表达式的匹配。
  • replace() String对象的方法,作用是替换与正则表达式匹配的子串。
  • \B是表示匹配非单词边界的元字符,与其互为补集的元字符是\b,表示匹配单词边界。

方案二: toLocaleString

toLocaleString() 方法返回一个该对象的字符串表示

覆盖 toLocaleString 的对象: Array: Array.prototype.toLocaleString()

Number: Number.prototype.toLocaleString()

Date: Date.prototype.toLocaleString()

Array.prototype.toLocaleString

概述: toLocaleString() 返回一个字符串 表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 ", ")隔开。 语法: arr.toLocaleString([locales[,options]])

参数: locales (可选) 参数用于指定格式化对象时使用的语言环境,默认为当前环境的语言, 一般而言使用 en 或 zh 即可应付绝大多数情况。 MDN : 带有BCP47语言标记的 字符串或字符串数组 ,关于locales参数的形式与解释,请看Intl页面 options (可选) 一个可配置属性的 对象

js
[1, 2, 3, 4].toLocaleString()
// "1,2,3,4"

var prices = ['¥7', 500, 8123, 12];
prices.toLocaleString('ja-JP', {
  style: 'currency',
  currency: 'JPY'
});
// "¥7,¥500,¥8,123,¥12"

至此, 将数组的 toLocaleString 方法理解为, 对数组每一项应用相应的 toLocaleString 方式, 然后返回组合的字符串, 即可.

Number.prototype.toLocaleString

其实不用传任何参数的时候, 千位分隔符的问题似乎就已经解决了.

js
const number = 123456789;
number.toLocaleString(); // 123,456,789

//对于中文场景下,toLocaleString('en-US')中的'en-US'理论上是可以缺省的,
//也就是直接(123456789).toLocaleString()也是可以得到123,456,789。
//但是如果你的产品可能海外用户使用,则保险起见,还是保留'en-US'。

如果传参数的时候, 是什么样子呢 ? ( 以下主要针对 options 参数, locales 参数一般使用 zh .) style 表示格式化时使用的样式,默认值是 decimal 也就是纯数字,也可为 percent 百分比显示与 currency 货币显示。值为 currency 时必须同时指定 options 中的 currency 属性,否则报错。

js
const number = 2333333;
number.toLocaleString('zh', {
  style: 'decimal'
}); //2,333,333
number.toLocaleString('zh', {
  style: 'percent'
}); //233,333,300%
number.toLocaleString('zh', {
  style: 'currency'
});
//Uncaught TypeError: Currency code is required with currency style.

接下来的两个属性是 style 设为 currency 时才有用的,它们分别是 currencycurrencyDisplay ,前者指定对应的货币,如 USDEURCNY 等,不区分大小写。后者是货币符号的展示样式,默认值是 symbol ,即对应的符号,如 CNY 。该属性的值也可以是 codename

js
const number = 2333333;
number.toLocaleString('zh', {
  style: 'currency',
  currency: 'CNY'
});
//¥2,333,333.00
number.toLocaleString('zh', {
  style: 'currency',
  currency: 'cny',
  currencyDisplay: 'code'
});
//CNY2,333,333.00
number.toLocaleString('zh', {
  style: 'currency',
  currency: 'cny',
  currencyDisplay: 'name'
});
//2,333,333.00人民币

最后是两组相当强大的属性,某些场景下能带来极大的便利。第一组是 minimumIntegerDigitsminimumFractionDigitsmaximumFractionDigits ,用于指定整数至少位数 与 小数的至少和至多位数,不够则用0去凑。简单说,自动补0!

js
let number = 2333.3;
//整数至少5位
number.toLocaleString('zh', {
  minimumIntegerDigits: 5
}); //02,333.3
//如果不想有分隔符,可以指定useGrouping为false
number.toLocaleString('zh', {
  minimumIntegerDigits: 5,
  useGrouping: false
});
//02333.3

//小数至少2位
number.toLocaleString('zh', {
  minimumFractionDigits: 2,
  useGrouping: false
}); //2333.30

//小数至多2位
number = 666.666
number.toLocaleString('zh', {
  maximumFractionDigits: 2,
  useGrouping: false
}); //666.67

拓展: ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。 padStart() 用于头部补全, padEnd() 用于尾部补全。详情戳阮一峰大大.

另一组是 minimumSignificantDigitsmaximumSignificantDigits ,用于控制 有效数字 位数,只要设置了这一组属性,第一组属性全部忽略不算

js
const number = 1234.5;
number.toLocaleString('zh', {
  minimumSignificantDigits: 7,
  useGrouping: false
}); //1234.500
number.toLocaleString('zh', {
  maximumSignificantDigits: 4,
  useGrouping: false
}); //1235

Date.prototype.toLocaleString

直接进入到 options 参数的讲解: hour12 表示是使用十二小时制还是二十四小时制,默认值视 locales 而定

js
const date = new Date();
date.toLocaleString('zh', {
  hour12: true
}); //"2018/5/9 下午3:20:07"
date.toLocaleString('zh', {
  hour12: false
}); //"2018/5/9 15:20:07"
date.toLocaleString('en', {
  hour12: true
}); //"5/9/2018, 3:20:07 PM"
date.toLocaleString('en', {
  hour12: false
}); //"5/9/2018, 15:20:07"

下面介绍其他属性 : weekdayera (时代)、 yearmonthdayhourminutesecond timeZoneName

weekdayera 可选值: narrowshortlong

js
const date = new Date();
date.toLocaleString('en', {
  weekday: 'narrow',
  era: 'narrow'
}); //W A
date.toLocaleString('en', {
  weekday: 'short',
  era: 'short'
}); //Wed AD
date.toLocaleString('en', {
  weekday: 'long',
  era: 'long'
}); //Wednesday Anno Domini

timeZoneName 可选值 shortlong

js
const date = new Date();
date.toLocaleString('zh', {
  timeZoneName: 'short'
}); // "2018/5/9 GMT+8 下午3:29:47"
date.toLocaleString('zh', {
  timeZoneName: 'long'
}); // "2018/5/9 中国标准时间 下午3:29:47"

剩下的属性,均可以取值为 numeric2-digit ,简单说就是否仅用两位数字表示

js
const date = new Date();
date.toLocaleString('zh', {
  year: 'numeric',
  month: 'numeric',
  day: 'numeric',
  hour: 'numeric',
  minute: 'numeric',
  second: 'numeric',
}); //"2018/5/9 下午3:32:15"
date.toLocaleString('zh', {
  year: '2-digit',
  month: '2-digit',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit'
}); //"18/05/09 下午3:32:15"
//比较奇怪的是 hour、minute 与 second 三个属性,无论设置为何值,表现都是一样的??

month 属性, 除去 numeric2-digit 外,它额外多三个属性,分别是 narrowshortlong

js
const date = new Date();
date.toLocaleString('zh', {
  month: 'long'
}); // "五月"
date.toLocaleString('zh', {
  month: 'short'
}); // "5月"
date.toLocaleString('zh', {
  month: 'narrow'
}); // "5"

扩展思考: toStringtoLocaleStringvalueOf 有什么区别?