2. 类型和值:String 和 Symbol

2. 类型和值:String 和 Symbol

  1. 学习 Unicode, utf-16, utf-8 是什么,有什么区别?
  • Unicode:

全球字符的统一的一个字符集。Unicode 字符集使得每一个字符都有一个整数编号。

编号规则:Unicode 将字符分为 17 个平面,第 1 个平面(称为基本多语言平面)到第 17 个平面(其他平面称为辅助平面)编号为 0(0x0) 到 16(0x10)。而对于每一个平面都有 2^16 (65536, 0x0000-0xFFFF)个字符,编号为 0 到 65535。因此对于每一个字符,它的 Unicode 编号是从 16 进制 0x000000 到 0x10FFFF,我们用 码位 表示为:U+0000 到 U+10FFFF。

值得注意的是:基本多语言平面内,从 U+D800(10 进制:55296) 到 U+DFFF(57343) 之间的码位(共 2048 个码位)是永久保留不映射到 Unicode 字符(utf-16 利用了这一点)。

对于这个外星人字符 👽,它的码位为 U+1F47D,所以它位于 17 个平面的第 2 个,且在这个平面中它的编号为 62589。我们可以使用 JavaScript 中的 Unicode 转义字符打印看看效果:

console.log('\u{1F47D}');

Unicode 只是规定了一个字符对应什么编号,但是这个编号在二进制的计算机中存储的时候,就必须使用字节的整数倍来表示。如果直接使用 Unicode 这种方式来存储,那么首先 17 个语言平面的编号需要一个字节(2^8 = 256),然后每个平面的 65536(2^16 = 2*(2^8))个字符需要 2 个字节。那么对于每一个字符总共就需要 3 个字节。这样的存储方式是很浪费空间的。

utf-8 和 utf-16 是另外 2 种存储方式,他们都使用了变长编码的概念,因此比较节省空间。

  • utf-16

(1) 对于基本多语言平面内(U+0000 到 U+FFFF,不包括前面提到的 U+D800 到 U+DFFF 之间的范围)的,因为一个平面就是 2^16 个字符,因此用 2 个字节就可以存储(在 UTF-16 中,2 个字节被称为一个 码元)。
(2) 对于辅助平面(U+010000 到 U+10FFFF)的,使用 4 个字节来存储,相当于 2 个 码元,这称为 代理对。前面的码元称为 前导代理,后面的码元称为 后导代理

具体的编码方法如下表:

前导代理/后导代理DC00(56320)DC01DFFF(56320+1023=57343)
D800(55296)1000010001103FF
D8011040010401107FF
DBFF(55296+1023=56319)10FC0010FC0110FFFF

从这个表格看到,前导代理范围是从 0xD800 到 0xDBFF,共 0xDBFF - 0xD800 + 1 = 56319 - 55296 + 1 = 1024 = 2^10 种情况,后导代理范围从 0xDC00 到 0XDFFF,共 0xDFFF - 0xDC00 + 1 = 57343 - 56320 + 1 = 1024 = 2^10种情况。因此,总共有这些情况 2^10 * 2^10 = 0x10FFFF-0x10000 + 1,恰好覆盖了辅助平面的码位。

可以看到,前导代理利用了基本多语言平面内没有被使用的空间:U+D800 到 U+DBFF,而后导代理刚好利用了剩下的空间:U+DC00 到 U+DFFF,这样巧妙地设计,辅助平面的编码方式虽然是 2 个码元,但是也不可能被识别成 2 个位于基本多语言平面的码元。

  • utf-8

(1) 128 个 US-ASCII 字符只需一个字节编码(Unicode 范围由 U+0000 至 U+007F)。
(2) 带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要两个字节编码(Unicode 范围由 U+0080 至 U+07FF)。
(3) 其他基本多文种平面(BMP)中的字符(这包含了大部分常用字,如大部分的汉字)使用三个字节编码(Unicode 范围由 U+0800 至 U+FFFF)。
(4) 其他极少使用的 Unicode 辅助平面的码位使用四字节编码(Unicode 范围由 U+10000 至 U+10FFFF)。

具体编码方法如下表:

码位二进制utf-8说明
U+0000-U+007F(128)00000000 00000000 0zzzzzzz0zzzzzzz(00-7F)ASCII 码位范围,字节从 0 开始
U+0080-U+07FF(1920)00000000 00000yyy yyzzzzzz110yyyyy(C0-DF) 10zzzzzz(80-BF)第一个字节由 110 开始,接着的字节由 10 开始
U+0800-U+D7FF/U+E000-U+FFFF(61440)00000000 xxxxyyyy yyzzzzzz1110xxxx(E0-EF) 10yyyyyy 10zzzzzz第一个字节由 1110 开始,接着的字节由 10 开始
U+010000-U+10FFFF(1048576)000wwwxx xxxxyyyy yyzzzzzz11110www(F0-F7) 10xxxxxx 10yyyyyy 10zzzzzz第一个字节由 11110 开始,接着的字节由 10 开始
  1. 解释下面代码所示的问题。
const text = '😀';
console.log(text.length);
// -> 2
// 为什么有一些字符的长度打印出来是 2?
// JavaScript String 类型底层使用的是 utf-16 吗?

JavaScript 底层将 String 类型的值解释一个由 UTF-16 码元组成的序列,而 length 属性只是反映了码元的数量。

在这个例子中,text 是一个表情字符,它不位于基本多语言平面,因此表示为一个代理对,即 2 个码元。因此打印出了 2。

  1. 学习 Symbol 类型,Symbol 类型可以怎么用?列举一些常见的周知符号,了解他们的作用。

Symbol 类型的用法:

  • 防止属性冲突
const obj = {};
const sym = Symbol('prop');

// 不会产生属性冲突
obj[sym] = 'something';
  • 枚举
const ButtonShape = {
  Rect: Symbol(0),
  Rounded: Symbol(1),
  Circle: Symbol(2),
};

// ...

switch (buttonShapes) {
  case ButtonShape.Rect:
    break;
  case ButtonShape.Rounded:
    break;
  case ButtonShape.Circle:
    break;
  default:
    break;
}

周知符号:

  • Symbol.toStringTag

这个周知符号在使用 Object.prototype.toString 方法进行类型判断时,可以被用到。

const foo = {
  [Symbol.toStringTag]: 'foo',
};

console.log(Object.prototype.toString.call(foo).slice(8, -1));
// -> 'foo'
  • Symbol.iterator

这个周知符号在实现可迭代对象时可以用到。

Number.prototype[Symbol.iterator] = function () {
  let i = 0;

  return {
    next: () => ({ value: i, done: i++ === this.valueOf() }),
  };
};

console.log(...10);
// -> 0 1 2 3 4 5 6 7 8 9
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰镇白干

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值