基本类型:String、Boolean、Number、Undefined、Null、Symbol、BigInt
引用类型:Object
1. String
储存结构
计算机是以二进制存储以及发送接收数据的。二进制的 1位,也叫做 1bit 。它是计算机内部存储的最基本的单位。
计算机只能处理数字,如果想要处理文本,必须将文本转换为数字才能处理,在计算机中 1bit 能表示2个状态(0或1),1Byte 等于 8bit,所以 1Byte 能表示 2^8 - 1 个整数,也就是255个。如果想表示更大的数字,就需要更多的字节数,比如 2Byte 能表示 2^16 - 1 ,也就是 65535个整数。最早只有127个字符被编码到计算机里,也就是大小写英文字母、数字和一些其他字符,这个编码表就是 ASCII 表。
但如果要表示中文,那么 1Byte 显然是不够的,至少需要 2Byte ,所以中国制定了 GB2312 编码,但每个国家如果都制定一个自己的编码表,那么就没办法正确显示多语言混合的文本。为了解决这个问题,Unicode 编码应运而生。它把所有语言统一到一个编码里,采用 2Byte 表示一个字符,即最多可以表示 2^16 - 1 ,也就是 65535 个字符。这样基本上可以覆盖世界上常用的文字,如果要表示更多的文字,也可以采用 4Byte 进行编码,这是一种通用的编码规范 。
JS 中的字符也采用Unicode编码,也就是js中的中英文字符都占用 2Byte(16bit)大小。
在 JS 中的二进制数据储存中,二进制前三位为 100
代表字符串
基本包装类型
在 js 中,只有引用类型才有属性或者方法,基本类型理论上没有属性或方法,而字符串又属于基本类型,但为什么字符串能调用一些属性和方法呢?
原因是 js 为了方便对字符串进行操作,ECMA 提供了一个基本包装类型 String对象。它是一种特殊的引用类型, 当 js 引擎需要读取或操作一个字符串时,他会在内部创建一个 String类型的包装对象实例,然后调用这个实例的属性或方法并返回结果后,再立即清除这个实例。
这也是为什么 字符串明明是一个基本类型 却能调用属性和方法的原因。
几个Unicode问题总结
基本概念
Unicode 是目前最常见的字符编码,它用一个码位映射一个字符。在 js 中,Unicode 码位范围为 '\u{0000}'
~ '\u{10ffff}'
,可以表示超过110万个字符。格式为 '\u{十六进制数字}'
console.log('\u{
0041}') // 'A'
console.log('\u{
0061}') // 'a'
console.log('I \u{
2661} Hagan') // 'I ♡ Hagan'
console.log('\u{
20bb7}') // '𠮷'
Unicode 最前面的 65536 个字符位称为 基本多文种平面,它的码位范围为 '\u{0000}'
~ '\u{ffff}'
,最常见的字符都放在这个平面上。
剩下的字符都放在 辅助平面 上,码位范围为 '\u{010000}'
~ '\u{10ffff}'
判断是否为辅助平面的方法为十六进制数字的位数是否超过4位。
字符串长度问题
- 解决代理对长度问题
在内部,JavaScript 将辅助平面内的字符表示为代理对,并将单独的代理对分开为单独的 “字符”,所以代理对的length属性可能与我们的预期不一致,比如:
const str = '\u{
20BB7}'
console.log(str) // '𠮷'
console.log(str.length) // 2
而我们想获取的长度应该为 1,这里可以使用以下方法正确获取长度
const str = '\u{
20BB7}'
console.log(Array.from(str).length) // 1
- 解决组合标记长度问题
\u{0307}
表示 q̣̇ 上面的点, \u{0323}
表示 q̣̇ 下面的点,这三个字符共同组成了一个 q̣̇ ,如下代码
const str = 'q\u{
0307}\u{
0323}'
console.log(str) // `q̣̇`
console.log(str.length) // 3
我们期待拿到的长度应该为1,可实际拿到的length为3,这里可以使用以下方法获取长度
const str = 'q\u{
0307}\u{
0323}'
const regex = /(\P{
Mark})(\p{
Mark}+)/gu
const trim = str.replace(regex, ($0, $1, $2) => $1)
console.log(Array.from(str).length) // 1
- 将以上代码封装起来,可封装成以下方法
注意:此方法无法处理表情字符序列组合 ‘👨👩👧👦’
const getStringLength = function (string) {
const regex = /(\P{
Mark})(\p{
Mark}+)/gu
const str = string.replace(regex, ($0, $1, $2) => $1)
return Array.from(str).length
}
export default getStringLength
字符串反转
注意:此方法无法正确处理组合标记
const getReverseString = function (string) {
return Array.from(string).reverse().join('')
}
export default getReverseString
一位名叫 Missy Elliot 的聪明的计算机科学家提出了一个防弹算法来解决组合标记问题
根据码位获取字符串
注意:此方法无法正确处理组合标记
String.fromCodePoint(0x20bb7) // '𠮷'
根据字符串获取码位
注意:此方法无法正确处理组合标记
'𠮷'.codePointAt().toString(16) // 20bb7
遍历字符串
注意:此方法无法正确处理组合标记
for (const item of '𠮷') {
console.log(item) // '𠮷'
}
Number
储存结构
js采用 IEEE754 标准中的 双精度浮点数来表示一个数字,标准规定双精度浮点数采用 64 位存储,即 8 个字节表示一个浮点数。存储结构如下图:
在双精度浮点数中,第一位的 1bit符号位 决定了这个数的正负,指数部分的 11bit 决定数值大小,小数部分的 52bit 决定数值精度。
在 JS 中的二进制数据储存中,二进制前三位为 010
代表双精度数字
数值范围
指数部分为 11bit 也就是 2^11 - 1 = 2047,取中间值进行偏移得到 [-1023, 1024],因此这种存储结构能够表示的数值范围为 2^-1023 至 2^1024,超出这个范围无法表示。转换为科学计数法为:
2^-1023 = 5 × 10^-324
2^1024 = 1.7976931348623157 × 10^308
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324
安全整数
IEEE754 规定,有效数字第一位默认总是1,但它不保存在 64 位浮点数之中。所以有效数字为 52bit + 1 = 53bit。
这意味着js能表示并进行精确算术运算的安全整数范围为 [-2^53 -1, 2^53 - 1] 即 -9007199254740991 到最大值 9007199254740991 之间的范围。
Math.pow(2, 53)