1. 前言
由于历史原因,在 JavaScript 创建之初,市面上的编码方式还是很混乱的,JavaScript 在创建之初,使用的是 1990 年公布的 UCS-2 的编码方法,使用 2 个字节表示 1 个字符,那时 UTF-8、UTF-16、UTF-32 还没有完全确定。现在的 JavaScript 主要使用的是 UTF-16 来存储的。
但针对于纷繁复杂的网页字符是不能完全地覆盖的,在早期使用浏览器时,经常会在浏览器中选择字符串编码方式。那么有没有一种编码可以涵盖世界上的所有字符呢?答案是有的 ——Unicode。它是一个字符集,它的定义很简单,用一个码点 (code point) 映射一个字符。码点值的范围是从 U+0000 到 U+10FFFF,可以表示超过 110 万个符号。
所以后来的 ECMAScript 一直致力于解决历史遗留的问题和统一浏览器的编码方式。这时 ES6 出来了,对 Unicode 进行了加强,也修复了 ES5 中的问题。在 模版字符串 的小节中已经学习了关于字符串模板字符串的内容,本节我们继续学习 ES6 中字符串其他的扩展。
2. Unicode
Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。所以出现了 Unicode 的多种存储方式,不同的实现导致了 Unicode 在很长一段时间内无法推广,而且本来英文字母只用一个字节存储就够了,如果 Unicode 统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是空,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
首先我们来看下一个字符是怎么表示的,JavaScript 提供了 charCodeAt()
获取指定位置的字符的值,返回的值在 0 到 65535 之间的整数。
var str = '码云'; console.log(str.charCodeAt(0)); // 30721 // 30721 转成十六进制 //String.fromCharCode(str.charCodeAt(0)).toUpperCase().codePointAt(0).toString(16);// 十六进制 0x7801 console.log(str.charCodeAt(1)); // 20113 转成十六进制 0x4e91 console.log(str.charCodeAt(2)); // NaN
看上面代码打印结果,并转成十六进制了,而 Unicode 表示是把前面的 0x 换成 u,这就是 Unicode 的表示。在 ES5 中还存在一个问题,实例如下:
let str = '?'; console.log(str.length) // 2 ,str 是 Emoji 表情符 console.log(str.charCodeAt(0)) // 55358 转成十六进制 0xd83e console.log(str.charCodeAt(1)) // 56618 转成十六进制 0xdd2a console.log('ud83eudd2a' === '?') // true
上面的代码可以看到 str 是一个 Emoji 的表情符号,使用 length 属性可以得到它的长度是 2,这完全不符合我们对这个字符串的定义。这就是 JavaScript 的编码问题。为解决 charCodeAt()
方法获取字符码位错误的问题,新增 codePointAt()
方法。
codePointAt()
方法完全支持 UTF-16,参数接收的是编码单元的位置而非字符位置,返回与字符串中给定位置对应的码位,即一个整数,如下实例:
let str = '?'; console.log(str.codePointAt(0)) // 129322 转成十六进制 0x1f92a => u1f92a console.log(str.codePointAt(1)) // 56618 转成十六进制 0xdd2a => udd2a
上面的代码中,第二行打印位置 0 处的编码单元开始的码位,此例是从这个编码单位开始的两个编码单元组合的字符(四个字节),所以会打印出所有码位,即四字节的码位 129322 即 0x1f92a,大于 0xffff,也证明了是占四个字节的存储空间。
3. 字符串的遍历器接口
ES6 为字符串添加了可遍历接口,使得字符串可以被 for...of
进行循环遍历。如下实例:
var str = '?'; for (let item of str) { console.log(item); } // 码 // 云 // 笔 // 记 // ?
上面的代码中,最后一个是 emoji 表情字符,存储时占 4 个字节,但是通过 for...of
可以正确地迭代为一个字符。在 ES5 中则不行,我们来看个实例,把上面的字符串使用 ES5 中的 split 方法把字符串转化成数组:
var str = '?'; console.log(str.split('')) // ['码', '云', '笔', '记', '?']
从上面的代码中可以清晰地反映出表情字符是占四个字节,但是,ES5 不能把它当作一个字节来处理所以就会出现数组后两个元素的样子。这也是 ES5 存在的主要问题之一,可以通过迭代器对复杂的字符串进行正确的处理。
4. 小结
ES6 还提供了其他的字符串操作的方法,接下来的几节专门讲解字符串中新增的方法,更好地解决实际的开发问题。本节主要讲解了,字符串对 Unicode 的增强,还有对字符串增加了遍历接口,非常实用。