@xiaoyixy
2018-11-08T17:05:36.000000Z
字数 5617
阅读 1092
Note
ES6
ECMAScript6
unicode:计算机科学领域里的一项业界标准,包括字符集、编码方案等。计算机采用八比特一个字节,一个字节最大整数是255,还要表示中文一个字也是不够的,至少需要两个字节,为了统一所有的文字编码,unicode为每种语言中的每个字符设定了统一并且唯一的二进制编码,通常用两个字节表示一个字符,所以unicode每个平面可以组合出65535种不同的字符,一共17个平面。
unicode 在计算机中用 unicode 字符集转换格式,即我们常见的 UTF-8、UTF-16 等。
UTF编码:全称是Unicode Transformer Format,这种编码是UCS(Universal Mutiple-Octet Doded Character Set,国际标准ISO10646规定的通用字符集)的实际形式,它的分类是按照其基本长度所占用的位数而定,分为UTF-8/16/32三种形式。UTF可以说是其他字符集的集合,它使得其它字符集是交叉兼容的,可以说,凡是将文字符号转为UCS后再转回原来的编码,也不会丢失信息。UCS包含了现在所有的已知语言的字符,包含从拉丁文、希腊语到中文、韩文等象形文字,再到日文的平假名、片假名等众多语系。因此使用UTF进行程序开发,绝对是程序国际化的首选,Unicode将世界的语言统一起来,构成了最伟大的字符集。
UTF-8 就是以字节为单位对 unicode 进行编码,对不同范围的字符使用不同长度的编码。
Unicode | Utf-8 |
---|---|
000000-00007F | 0xxxxxxx |
000080-0007FF | 110xxxxx 10xxxxxx |
000800-00FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
010000-10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
// 汉字“编” \u7f16 的 utf-8 编码
1. 7f16 在 0800-FFFF 之间,所以要用3字节模板:1110xxxx 10xxxxxx 10xxxxxx
2. 7f16 写成二进制是:0111 1111 0001 0110
3. 按三字节模板分段方法分为0111 111100 010110,代替模板中的x,得到11100111 10111100 10010110
4. 即 “编” 对应的utf-8的编码是e7 bc 96,占3个字节
JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为 2 个字节。对于那些需要 4 个字节储存的字符(Unicode 码点大于0xFFFF的字符),JavaScript 会认为它们是两个字符。
codepoint: 与一个Unicode编码表中的某个字符对应的代码值。
JavaScript 允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码点。
"\u0061" // "a"
但是,这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示。如果直接在\u后面跟上超过0xFFFF的数值(比如\u20BB7),JavaScript 会理解成\u20BB+7。
"\uD842\uDFB7" // "𠮷"
"\u20BB7" // "₻7"
ES6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。
"\u{20BB7}" // "𠮷"
"\u{41}\u{42}\u{43}" // "ABC"
let hello = 123;
hell\u{6F} // 123
'\u{1F680}' === '\uD83D\uDE80' // true
JavaScript 共有 6 种方法可以表示一个字符。
'\z' === 'z' // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true
方法返回一个 Unicode 编码点值的非负整数。于 ECMAScript 2015 (6th Edition, ECMA-262) 标准化。
'ABC'.codePointAt(1); // 66
'\uD800\uDC00'.codePointAt(0); // 65536
'XYZ'.codePointAt(42); // undefined
codePointAt方法返回的是码点的十进制值,如果想要十六进制的值,可以使用toString方法转换一下。
let s = '𠮷a';
s.codePointAt(0).toString(16) // "20bb7"
s.codePointAt(2).toString(16) // "61"
// for...of循环会正确识别 32 位的 UTF-16 字符。
for (let ch of s) {
console.log(ch.codePointAt(0).toString(16));
}
// 20bb7
// 61
codePointAt方法是测试一个字符由两个字节还是由四个字节组成的最简单方法。
function is32Bit(c) {
return c.codePointAt(0) > 0xFFFF;
}
is32Bit("𠮷") // true
is32Bit("a") // false
该静态方法返回使用指定的代码点序列创建的字符串。在作用上,正好与codePointAt方法相反。如果传入无效的 Unicode 编码,将会抛出一个RangeError (例如: "RangeError: NaN is not a valid code point")。
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
ES5 提供 String.fromCharCode() 方法,用于从码点返回对应字符,但是这个方法不能单独获取在高代码点位上的字符,不能识别 32 位的 UTF-16 字符(Unicode 编号大于 0xFFFF)。
String.fromCharCode(0x20BB7) // "ஷ"
String.fromCharCode(0x0BB7) // "ஷ"
String.fromCodePoint(0x20BB7) // "𠮷"
for...of
语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代锚点,并为每个不同属性的值执行语句ES6 为字符串添加了遍历器接口,使得字符串可以被 for...of 循环遍历。该接口可以识别大于 0xFFFF 的码点,传统的 for 循环无法识别这样的码点。
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"
let text = String.fromCodePoint(0x20BB7);
for (let i = 0; i < text.length; i++) {
console.log(text[i]);
}
// " "
// " "
for (let i of text) {
console.log(i);
}
// "𠮷"
str.normalize([form])
按照指定的一种 Unicode 正规形式将当前字符串正规化
'\u01D1' // "Ǒ"
'\u004F\u030C' // "Ǒ"
'\u01D1'.length // 1
'\u004F\u030C'.length // 2
'\u01D1'==='\u004F\u030C' //false
'\u01D1'.normalize() === '\u004F\u030C'.normalize() // true
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
`string text`
// 多行字符串
`string text line 1
string text line 2`
// 插入表达式
`string text ${expression} string text`
// 带标签的模板字符串
// 标签函数并不一定需要返回一个字符串
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Nesting_templates
tag `string text ${expression} string text`
// 嵌套模板
const classes = `header ${ isLargeScreen() ? '' :
`icon-${item.isCollapsed ? 'expander' : 'collapser'}` }`;
let message =
SaferHTML`<p>${sender} has sent you a message.</p>`;
function SaferHTML(templateData) {
let s = templateData[0];
for (let i = 1; i < arguments.length; i++) {
let arg = String(arguments[i]);
// Escape special characters in the substitution.
s += arg.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
// Don't escape special characters in the template.
s += templateData[i];
}
return s;
}
在标签函数的第一个参数中,存在一个特殊的属性raw ,我们可以通过它来访问模板字符串的原始字符串,而不经过特殊字符的替换。
function tag(strings) {
console.log(strings.raw[0]);
}
tag`string text line 1 \n string text line 2`;
// logs "string text line 1 \n string text line 2" ,
// including the two characters '\' and 'n'
var str = String.raw`Hi\n${2+3}!`;
// "Hi\n5!"
str.length;
// 6
str.split('').join(',');
// "H,i,\,n,5,!"
这表示类似下面这种带标签的模版是有问题的,因为对于每一个ECMAScript语法,解析器都会去查找有效的转义序列,但是只能得到这是一个形式错误的语法:
latex`\unicode`
// 在较老的ECMAScript版本中报错(ES2016及更早)
// SyntaxError: malformed Unicode character escape sequence
ES2018关于非法转义序列的修订
ES2018 放松了对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义,就返回undefined,而不是报错。值得注意的是,这一转义序列限制只对带标签的模板字面量移除,而不包括不带标签的模板字面量:
let bad = `bad escape sequence: \unicode`;
// SyntaxError: Invalid Unicode escape sequence