6

1 四字节字符

ES6大幅增强了对4字节(32位)字符的支持。

JS内部使用UTF-16编码规则(网页通常为UTF-8)。
1字符固定为2字节,1字节为8位(二进制),其码点小于0xFFFF
有些符号的码点大于0xFFFF,需4字节表示,即常说的32位UTF-16字符。

1.1 表示方法

新增一种表示字符的方法:\u{20BB7}
{}将码点括起,使其可直接表示超过0xFFFF的值。

let str1 = '𠮷'; // 非吉,虽也读作 jí 。
let str2 = '\u{20BB7}';
let str3 = '\uD842\uDFB7';

console.log(str1 === str2); // true
console.log(str2 === str3); // true

4字节字符的两种表示有固定的转化规则,不是简单的相加。

let c = 0x20BB7;
let ch = 0xD842;
let cl = 0xDFB7;

console.log( H(Number(c)) === ch ); // true
console.log( L(Number(c)) === cl ); // true

function H(c) {
  return Math.floor((c - 0x10000) / 0x400) + 0xD800;
}
function L(c) {
  return (c - 0x10000) % 0x400 + 0xDC00;
}

1.2 codePoint 系列

codePointAt & charCodeAt
使用.codePointAt(0)正确读取到完整的4字节字符。

let str = '𠮷';

console.log( str.charCodeAt(0) ); // 55362,即 \uD842 。
console.log( str.charCodeAt(1) ); // 57271,即 \uDFB7 。
console.log( str.codePointAt(0) ); // 134071,即 \u{20BB7} ,即 𠮷。
console.log( str.codePointAt(1) ); // 57271,即 \uDFB7 。

fromCodePoint & fromCharCode
使用String.fromCodePoint()正确读取大于0xFFFF的32位字符的码点。

console.log( String.fromCharCode(0x20BB7) ); // 解析不成功,已超过 0xFFFF 。
console.log( String.fromCodePoint(0x20BB7) ); // '𠮷',即'\u{20BB7}'。

console.log( String.fromCharCode(0xD842) + String.fromCharCode(0xDFB7) ); // '𠮷',即 '\uD842\uDFB7' 。
console.log( String.fromCodePoint(0xD842) + String.fromCodePoint(0xDFB7) ); // '𠮷',即 '\uD842\uDFB7' 。

1.3 常用方法集

判断是否为32位字符

console.log(is32Bit('a')); // false
console.log(is32Bit('𠮷')); // true

function is32Bit(s) {
  return s.codePointAt(0) > 0xFFFF;
}

获取正确的字符长度

let str = 'a𠮷b';

console.log( getStringLength1(str) ); // 3
console.log( getStringLength2(str) ); // 3

function getStringLength1(str) {
  let res = str.match(/[\s\S]/gu);
  return res ? res.length : 0;
}
function getStringLength2(str) {
  let res = 0;
  for (let c of str) { res++; } // 此遍历方法可以正确的识别32位字符。
  return res;
}

遍历方法
ES5由于不能正确的识别32位字符,遍历存在隐患。
ES6可以配合for ofcodePointAt更为正确的遍历字符串。

依次打印出:a 𠮷 b。

let str = 'a𠮷b';
for (let c of str) {
  console.log( String.fromCodePoint( c.codePointAt(0) ) );
}

2 模板字符串

在现行项目中,一般推荐使用模板字符串代替单双引号作为定义字符串的统一规范。

模板字符串是ES6新增的定义字符串的方式,使用 ` 替代''""`。
其字符串中的空格、缩进和换行等空白字符都会被保留在输出之中。
其字符串中可以使用${}包裹有待执行的任意JS代码,包括另一个模板字符串。

let name = 'Wmaker';
let str = `
<div class="header">
  <p>My name is ${ name }.</p>
</div>
`;

console.log(str);
// 输出:
// 
// <div class="header">
//   <p>My name is Wmaker.</p>
// </div>

2.1 标签模板

标签模板是函数调用的一种特殊形式: fn`string`

实际传入fn的参数有三类。
一是模板字符串会以/${([\s\S]+?)}/g为分隔符,拆分形成第一个参数。
二是将各个${}(输出变量)作为依次的参数。
三是第一个参数中的raw属性,这个稍后分析。

let words = 'hi';
let name = 'Wmaker';

say`Say ${words} to ${name}`;
// --- 等价于
say.apply(null, [
  ['Say ', ' to ', ''],
  'hi',
  'Wmaker'
]);

function say() {
  console.log(arguments);
}

raw属性
其是为了方便取得转义之前的原始模板而设计的。
其是与第一个参数基本相同,只不过里面的项都是转义之前的(相当再次转义)。

fn`1\n2`;
// --- 等价
fn.apply(null, ['1↵2', raw:['1\n2']]);

实际应用
标签模板的优势在于,不同场景不同参数都可使用同一编译模式。

let words = 'hi';
let name = 'Wmaker';

compileTemplate`<p>Say ${words} to ${name}.</p>`; // &lt;p&gt;Say hi to Wmaker.&lt;/p&gt;
compileTemplate`<p>Hi ${name}!</p>`; // &lt;p&gt;Hi Wmaker!&lt;/p&gt;

function compileTemplate() {
  let strs = arguments[0];
  let vars = Array.prototype.slice.call(arguments, 1);
  let res = transformHTML(strs[0]);

  vars.forEach((item, i) => {
    res += item + transformHTML(strs[i+1]);
  });
  
  console.log(res);

  function transformHTML(s) {
    return s.replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;");
  }
}

3 新增函数

这里只列举一些常用的方法和方式,具体可看:String API

3.1 normalize()

方法会按照一种指定的Unicode正规形式将当前字符串正规化。
有些字符有两种不同的表示方法,而且其表示不等价,虽然在结果上等价。
比如有些语言有语调符号和重音符号Ǒ,可以使用带重音符号的字符和原字符与重音符两种表示方法。

let s1 = '\u01D1'; // 'Ǒ'
let s2 = '\u004F\u030C'; // 'Ǒ'

console.log(s1 === s2); // false
console.log(s1.normalize() === s2.normalize()); // true

3.2 padStart() & padEnd()

从头/尾部补全字符的长度,对填充物的态度是多则重复少则裁剪。

为数值补全指定位数。
'1'.padStart(5, '0'); // "00001"

提示字符串格式。
'12'.padStart(10, 'YYYY-MM-DD'); // "YYYY-MM-12"

3.3 String.raw()

用来充当模板字符串的处理函数,返回一个斜杠都被转义且变量已被替换的字符串。

let name = 'Wmaker';
let str = String.raw`
<div class=\"header\">
  <h2>${name}</h2>
</div>
`;

console.log(str);
// 输出:
// 
// <div class=\"header\">
//   <h2>Wmaker</h2>
// </div>

wmaker
2.9k 声望4.7k 粉丝

保持节奏。