在 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 ('\uD800\uDC00', 0); // 65536 基础平面是 0~65535之间
fixedCharCodeAt ('\uD800\uDC00', 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" == "\uD83C\uDF03"
String.fromCharCode(0xD834, 0xDF06, 0x61, 0xD834, 0xDF07); // "\uD834\uDF06a\uD834\uDF07"
String.fromCodePoint(num1, num2....)
fromCodePoint 字符串静态方法,用于将 unicode 码点转为对应的字符串。如果传入无效的 Unicode 编码,将会抛出一个 RangeError 的异常。
String.fromCodePoint(42); // "*"
String.fromCodePoint(65, 90); // "AZ"
String.fromCodePoint(0x404); // "\u0404"
String.fromCodePoint(0x2F804); // "\uD87E\uDC04"
String.fromCodePoint(194564); // "\uD87E\uDC04"
String.fromCodePoint(0x1D306, 0x61, 0x1D307) // "\uD834\uDF06a\uD834\uDF07"
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。 |