Skip to content
On this page

基本类型

没有值的空类型

假设我们要创建一个工具库,要定义一个函数:给定错误信息,输出错误的时间戳和信息,这个函数是对 throw 关键字的封装,所以不应该返回值:

ts
function raise(message: string): never {
    console.error(`Error at ${new Date()}`);
    throw new Error(message)
}

此函数返回类型为 never,这代表着这个函数从不会返回,编译器会保证这个函数不会有返回语句,因为任何值都无法被赋值给 never。

这种类型被称为不可赋值类型或空类型,因为我们无法创建它的实例。我们通常用空类型表示不可能,例如用作不会返回的函数(抛出异常或无限循环)。

大多数编程语言用 void 来表示不存在有意义的值,但是将 raise() 那样的函数声明为返回 void 具有误导性,因为 raise() 不是不返回有意义的值,而是根本不返回。

只有一个值的单元类型

有些函数确实会返回,但是不会返回有意义的值,执行这种函数只为了获得它们的副作用:这些函数会执行某种操作,修改一些外部状态,但不会执行有意义的计算返回给我们。这时候就需要返回单元类型,即 void。

有两个值的布尔类型

布尔值提供两个取值,truefalse,同时提供三种逻辑运算:ANDORNOT,一些类型系统依靠数字表示布尔值,0 为 false,1 为 true,也有 Access 数据库这种逆天玩意把 0 当做 false,-1 当成 true 的。

大部分语言的布尔表达式中,&& 代表 AND,|| 代表 OR,! 代表 NOT。

数值类型及陷阱

在多数编程语言中,通常会提供多种基本类型来表示数字。取决于使用数值类型的形式,我们可能会得到意外的结果,比如下面这个大名鼎鼎的梗:

ts
if ((0.1 + 0.1 + 0.1) != 0.3) {
    console.log("Fuck you! JavaScript!")
}
// Thank you! JavaScript!

整数类型和溢出

无符号二进制编码使用每个位来表示值的一部分。例如一个四位无符号整数可以表示 0-15 之间的任意值,一般来说,一个 N 位无符号整数可以表示 0 到 $2^N-1$ 的值。

四位无符号整数
00000
10001
101010
151111

这样编码直观,但是只能表示正整数,如果想要表示负数,就需要不同的编码。

一般我们用补码编码,我们保留第一位作为符号位,正数的表示跟前面一样,但负数编码是从 $2^n$ 减去它的绝对值,其中 N 是位数。

四位带符号编码
-81000
-31101
00000
30011
70111

其中 -8 被编码为 $2^4 - 8$,-3 被编码为 $2^4 - 3$。

上溢和下溢

当运算的结果超过可以表达的极限,就称这是算术上溢或者算术下溢。编程语言处理这种情况的方法大致有这么几种:

  1. 环绕方式:丢弃不合适的位,4 位无符号整数 1111 加 1,等于 10000,我们只需要四位,所以把 1 丢弃,变成 0000,这是最高效的方法,同时也是最危险的方式,比如我有 15 块钱,加一就变成了 0
  2. 饱和方式:就简单的达到最高之后就不增加了,4 位无符号整数 1111 再加一仍然等于 1111
  3. 报错方式:溢出就直接抛出错误,这是最安全的方法,但是需要在运算的时候处理异常

浮点类型

IEEE 754 标准是美国电气和电子工程师协会为表示浮点数制定的标准,在 TypeScript 和 JavaScript 中使用 binary64 编码将数字表示为 64 位浮点数。

跟上面的一样,浮点数同样是第一位做符号位,同样是 0 代表正数,1 代表负数,之后是指数,用十一位二进制整数表示 -1024 ~ 1023,最后是尾数,因为 JavaScript 的浮点数是 64 位的,因此有 52 位,$2^52=4503599627370496$,总共 16 位,因此表示十进制数的精度就有 16 位。

因此如果需要处理精度,则应该避免使用浮点数,比如在计算货币的场景,把元、角、分给分开计算。

所以为什么 0.2+0.1=0.3 呢,一言以蔽之:用二进制计算十进制的时候的精度问题,剩下的我讲不清,似懂非懂。所以,要高精度计算这种事情,还是交给 Math.js 这种库吧,我惹不起。

编码文本

字符串是一个可以编码 0 到无限个值的类型,最早计算机用一个字节表示一个字符,所以最多能表示 256 个字,因为 $2^8=256$,但是后面发生的事情你应该也猜到了,光我这篇文章出现的字符就不止 256 种,所以 Unicode 标准出现了,Unicode 一开始定义两个字节一个字符,总共 65536 个字符,但是还是不够(光汉文就十万种字符了,虽然汉文也没有全部录进去就是了),所以又出了一个 UTF-32,但是每个都占用 4 个字节实在是太慢了,所以出现了变长编码,就是 UTF-8 和 UTF-16。所以现在 Unicode 标准中最常用的 utf-8 支持最多四个字节,通过 1~4 个字节来表示字符,$2^{32}=16777216$ 这回暂时是够了,但是很遗憾,JavaScript 用的是 UTF-16,它通过 2 或 4 个字节表示字符,差了点但是够用了。

emoji 的处理

一个女警 emoji 占用的宽度是 5 字节,不信就来试试:

ts
console.log("👮‍♀️".length) // 5

因为女警是又两个 emoji 和一个链接符号组成的,第一个男警察(U+1F46E)是两个字节,再加上链接符(U+200D)是一个字节,最后加上女性符号(U+2640 U+FEOF) 两个字节。同时还有黑人女警这种七个字节 emoji,都是这么叠出来的,因为实在太占空间。

Released under the MIT License.