- 那么如何将一个字符串转为一个 unicode 码点。
- 那么如何将一个 unicode 码点转为一个字符串?
- String.fromCodePoint(num1, num2….)
- String.prototype.charAt(index)
- 比较:
在 JavaScript 中,字符串使用了 UTF-16 编码,该编码使用一个 16 比特的编码单元来表示大部分常见的字符,使用两个代码单元表示不常用的字符。
在 JavaScript 中 unicode
是以十六进制代码外加转义字符 “u” 来表示的字符串。
"uxxxx"
使用 “u” 来对一个 Unicode 来进行解码,获得字符串。
那么如何将一个字符串转为一个 unicode 码点。
String.prototype.charCodeAt(index)
charCodeAt
字符串的实例方法,返回 0 ~ 65535
之间的整数,表示给定索引处的 UTF-16 代码单元。如果 index,不是一个数值,就默认是 0,如果超出范围,返回 NaN。
index |
index 转为数值,如果 Number 转换中不是一个数字,默认 index 是 0. |
如果 index 不在 0~str.length 范围,返回 NaN。 |
注意,charCodeAt
只能表示基础平面那的 unicode
码点,对于辅助平面内的码点是不能表示的。
所以我们为了表示出辅助平面的码点,我们需要借助代理对,使用高位+地位来构成一个真正的字符。但是,这会造成一个问题,就是我们获取的码点也是代理过去的码点,即 0xD800~0xDB00
与 0xDC00~0xDFFF
两个之间的码点。
那么我们怎么才能修复一下 charCodeAt
,来让它支持出现 在辅助平面的码点并返回。
// 下面的代码是出现了未知的非基础平面的范围。 function fixedCharCodeAt (str, idx) { idx = idx || 0; // 默认不存在会是 0。 var code = str.charCodeAt(idx); // 先获取 基础平面内的码点。 var hi, low; // 高位 and 地位。 if (0xD800 <= code && code <= 0xDBFF){ // 高位区间 hi = code; low = str.charCodeAt(idx + 1); // 代理对的第二个 低位。 if (isNaN(low)) { // index 超出长度会返回 NaN throw 'High surrogate not followed by low surrogate in fixedCharCodeAt()'; } return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; } if (0xDC00 <= code && code <= 0xDFFF) { hi = str.charCodeAt(idx-1); low = code; return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; } return code; // 超出了范围返回 NaN,以及就是返回基础平面的码点。 } fixedCharCodeAt ('uD800uDC00', 0); // 65536 基础平面是 0~65535 之间 fixedCharCodeAt ('uD800uDC00', 1); // 65536
对于上面的 ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000
,我们其实是利用了一个反推,在 Unicode 3.0 给出了辅助平面去求得基本平面映射的公式。Unicode 3.0 公式,我们可以从基础平面公式 ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000
反推回去得到一个辅助平面的 unicode 码点。
那么如何将一个 unicode 码点转为一个字符串?
String.fromCharCode(num1,num2....)
fromCharCode 字符串静态方法,用于将 unicode 码点转为对应的字符串。
参数范围在 0 ~ 65535 之间,大于的不做检查,比如 0x12345 —> 0x2345,返回一个字符串。
String.fromCharCode(65, 66, 67); // 返回 "ABC" String.fromCharCode(0x2014); // 返回 "—" String.fromCharCode(0x12014); // 也是返回 "—"; 数字 1 被剔除并忽略 String.fromCharCode(8212); // 也是返回 "—"; 8212 是 0x2014 的十进制表示
String.fromCharCode(0xD83C, 0xDF03); // Code Point U+1F303 "Night with String.fromCharCode(55356, 57091); // Stars" == "uD83CuDF03" String.fromCharCode(0xD834, 0xDF06, 0x61, 0xD834, 0xDF07); // "uD834uDF06auD834uDF07"
String.fromCodePoint(num1, num2….)
fromCodePoint
字符串静态方法,用于将 unicode 码点转为对应的字符串。如果传入无效的 Unicode 编码,将会抛出一个 RangeError 的异常。
String.fromCodePoint(42); // "*" String.fromCodePoint(65, 90); // "AZ" String.fromCodePoint(0x404); // "u0404" String.fromCodePoint(0x2F804); // "uD87EuDC04" String.fromCodePoint(194564); // "uD87EuDC04" String.fromCodePoint(0x1D306, 0x61, 0x1D307) // "uD834uDF06auD834uDF07" String.fromCodePoint('_'); // RangeError String.fromCodePoint(Infinity); // RangeError String.fromCodePoint(-1); // RangeError String.fromCodePoint(3.14); // RangeError String.fromCodePoint(3e-2); // RangeError String.fromCodePoint(NaN); // RangeError
它和 String.fromCharCode 的区别就是 它可以识别辅助平面的码点,并返回指定的字符。
String.fromCodePoint(0x2F804)
// Polyfill: 它是 ECMASscript 2015 新增的特性。 if (!String.fromCodePoint) (function(stringFromCharCode) { var fromCodePoint = function(_) { var codeUnits = [], codeLen = 0, result = ""; for (var index=0, len = arguments.length; index !== len; ++index) { var codePoint = +argument[index]; // +会进行隐式强制转换,Number()转为一个数 // `NaN`, `-Infinity`, `+Infinity` // 表示码点不会大于 辅助平面最大 0x10FFFF 并且 codePoint>>>0 小数和负数将会变为 // 另一个数,所以不会等于本身。 if (!(codePoint < 0x10FFFF && (codePoint>>>0) === codePoint)) { throw RangeError("Invalid code point: " + codePoint); } if (codePoint <= 0xFFFF) { // BMP codeLen = codeUnits.push(codePoint); // 基础平面的码点 } else { codePoint -= 0x10000; codeLen = codeUnits.push( (codePoint >> 10) + 0xD800, // 高位 (codePoint % 0x400) + 0xDC00 // 低位 ); } if (codeLen >= 0x3fff) { // 参数超过 0x3fff,就不需要在寸了。 result += stringFromCharCode.apply(null, codeUnits); codeUnits.length = 0; } } return result + stringFromCharCode.apply(null, codeUnits); } try { // IE 8 支持下面这种添加静态属性。 Object.defineProperty(String, "fromCodePoint", { "value": fromCodePoint, "configurable": true, "writable": true }); } catch(e) { // 如果上面不成功,我们将直接赋值即可。 String.fromCodePoint = fromCodePoint; } })(String.fromCharCode);
关于有符号位移 >> 和 无符号位移 >>>,可以查看 位移
String.prototype.charAt(index)
charAt
方法可以将一个字符串返回指定的字符。 index
的范围是 0~length-1
之间,如果不提供,默认使用 0。如果指定的 index 值超出了该范围,则返回一个空字符串。
var anyString = "Brave new world"; console.log("The character at index 0 is '" + anyString.charAt(0) + "'"); console.log("The character at index 1 is '" + anyString.charAt(1) + "'"); console.log("The character at index 2 is '" + anyString.charAt(2) + "'"); console.log("The character at index 3 is '" + anyString.charAt(3) + "'"); console.log("The character at index 4 is '" + anyString.charAt(4) + "'"); console.log("The character at index 999 is '" + anyString.charAt(999) + "'");
我们在获取字符的长度时,会把辅助平面的看作是两个长度。
function getChar(str, i) { var code = str.charCodeAt(i); // 先获取码点。 if (isNaN(code)) { return ''; // 不在范围内,返回空字符. } if (code < 0xD800 || code > 0xDFFF) { // 本来就是基础平面的码点。 return str.charAt(i); // 直接返回字符 } if (0xD800 <= code && code <= 0xDBFF) {. // 高位在范围内 if (str.length <= (i+1)) { // 是否还存在低位 throw 'High surrogate without following low surrogate'; } var next = str.charCodeAt(i + 1); if (0xDC00 > next || next > 0xDFFF) { // 低位不在范围内 throw 'High surrogate without following low surrogate'; } return str.charAt(i) + str.charAt(i + 1); // 返回高位 + 低位 } if (i === 0) { // 第一位就是低位 throw 'Low surrogate without preceding high surrogate'; } var prev = str.charCodeAt(i - 1); // 高位 if (0xD800 > prev || prev > 0xDBFF) {. // 高位不在范围 throw 'Low surrogate without preceding high surrogate'; } return false; // 高位存在,低位也存在,低位的字符就不需要在返回出来,统一在高位返回。 }
比较:
charAt | charCodeAt |
参数 Number 转换,转换后不是一个数值,就使用默认值 0,获取第一个字符。 | 参数 Number 转换,转换后不是一个数值,就使用默认值 0,获取第一个字符。 |
返回值是一个字符,只要参数不是 0 ~length – 1,包括 Infinity 返回空字符。 | 返回值是一个字符,只要参数不是 0 ~length – 1,包括 Infinity 返回 NaN。 |
fromCharCode | fromCodePoint |
参数 Number 转换,转换后不是一个数值,就会返回 “x00″。 | 参数 Number 转换,转换后不是一个数值,就会抛出异常 RangeError。 |