ECMAScript6/ES6 入門
五、字串的擴充套件
注:這章沒什麼可以理解的,看過一遍就行,所以大體上我把有用的一些東西拷過來給大家看下。
1、字元的Unicode表示法
JavaScript 允許採用uxxxx
形式表示一個字元,其中xxxx表示字元的 Unicode 碼點。但是,這種表示法只限於碼點在u0000
~uFFFF
之間的字元。超出這個範圍的字元,必須用兩個雙位元組的形式表示。如果直接在u後面跟上超過0xFFFF的數值(比如u20BB7),JavaScript 會理解成u20BB+7。由於u20BB是一個不可列印字元,所以只會顯示一個空格,後面跟著一個7。
"uD842uDFB7"
// "?"
"u20BB7"
// " 7"
在ES6中做了處理,只要將碼點放入大括號,就能正確解讀該字元。有了這種表示法之後,JavaScript 共有 6 種方法可以表示一個字元。
"u{20BB7}"
// "?"
"u{41}u{42}u{43}"
// "ABC"
let hello = 123;
hellu{6F} // 123
`u{1F680}` === `uD83DuDE80`
// true
`z` === `z` // true
`172` === `z` // true
`x7A` === `z` // true
`u007A` === `z` // true
`u{7A}` === `z` // true
2、codePointAt()
對於原來的charAt
和charCodeAt
,是無法獲取4個位元組的字串的,charCodeAt
方法只能分別返回前兩個位元組和後兩個位元組的值。而有些漢字是需要4個位元組的,比如:漢字“?”(注意,這個字不是“吉祥”的“吉”)的碼點是0x20BB7
。
var s = "?";
s.length // 2
s.charAt(0) // ``
s.charAt(1) // ``
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271
所以在ES6中提供了codePointAt
方法,能夠正確返回4個位元組儲存的字串。
let s = `?a`;
s.codePointAt(0) // 134071
s.codePointAt(1) // 57271
s.codePointAt(2) // 97
有的人就要問了,codePointAt
方法為啥是3個字元(0,1,2)?首先,JavaScript
肯定是將`?a`視作3個字元的,只是ES6方法不同而已,codePointAt(0)
返回的是第一字元`?`,codePointAt(1)
返回的是`?`(共4個位元組)的後兩個位元組,codePointAt(2)
返回的是`a`。也就是說後兩個位元組,codePointAt
方法的結果與charCodeAt
方法相同。
同時,從上面的例子可以看出,codePointAt方法返回的是碼點的十進位制值,如果想要十六進位制的值,可以使用toString方法轉換一下。
let s = `?a`;
s.codePointAt(0).toString(16) // "20bb7"
s.codePointAt(2).toString(16) // "61"
可能這樣看上去感覺怪怪的,畢竟只有兩個字元,寫的時候還要去注意0,2,其實有更好的解決辦法,利用for...of
迴圈,直接看例子:
let s = `?a`;
for (let ch of s) {
console.log(ch.codePointAt(0).toString(16));
}
// 20bb7
// 61
3、String.fromCodePoint()String.fromCharCode
用於從碼點返回對應字元,但是這個方法不能識別 32
位的 UTF-16
字元(Unicode
編號大於0xFFFF
)。
String.fromCharCode(0x20BB7)
// "ஷ"
ES6
提供了String.fromCodePoint
方法,可以識別大於0xFFFF
的字元,彌補了String.fromCharCode
方法的不足。在作用上,正好與codePointAt
方法相反。
String.fromCodePoint(0x20BB7)
// "?"
String.fromCodePoint(0x78, 0x1f680, 0x79) === `xuD83DuDE80y`
// true
4、字串的遍歷器介面
for (let codePoint of `foo`) {
console.log(codePoint)
}
// "f"
// "o"
// "o"
for...of
迴圈遍歷,這個遍歷器最大的優點是可以識別大於0xFFFF的碼點,傳統的for迴圈無法識別這樣的碼點。
5、at()
同上,charAt
不能識別碼點大於0xFFFF
的字元。at
則可以。
`abc`.at(0) // "a"
`?`.at(0) // "?"
6、normalize()normalize()
方法,用來將字元的不同表示方法統一為同樣的形式,這稱為 Unicode
正規化。
許多歐洲語言有語調符號和重音符號。為了表示它們,Unicode
提供了兩種方法。一種是直接提供帶重音符號的字元,比如Ǒ(u01D1)
。另一種是提供合成符號(combining character
),即原字元與重音符號的合成,兩個字元合成一個字元,比如O(u004F)
和ˇ(u030C)
合成Ǒ(u004Fu030C)
。
`u01D1`===`u004Fu030C` //false
`u01D1`.normalize() === `u004Fu030C`.normalize()
// true
normalize方法可以接受一個引數來指定normalize的方式,引數的四個可選值如下。
NFC,預設引數,表示“標準等價合成”(Normalization Form Canonical Composition),返回多個簡單字元的合成字元。所謂“標準等價”指的是視覺和語義上的等價。
NFD,表示“標準等價分解”(Normalization Form Canonical Decomposition),即在標準等價的前提下,返回合成字元分解的多個簡單字元。
NFKC,表示“相容等價合成”(Normalization Form Compatibility Composition),返回合成字元。所謂“相容等價”指的是語義上存在等價,但視覺上不等價,比如“囍”和“喜喜”。(這只是用來舉例,normalize方法不能識別中文。)
NFKD,表示“相容等價分解”(Normalization Form Compatibility Decomposition),即在相容等價的前提下,返回合成字元分解的多個簡單字元。
7、includes(), startsWith(), endsWith() indexOf
方法,可以用來確定一個字串是否包含在另一個字串中。ES6 又提供了三種新方法。
includes():返回布林值,表示是否找到了引數字串。
startsWith():返回布林值,表示引數字串是否在原字串的頭部。
endsWith():返回布林值,表示引數字串是否在原字串的尾部。
let s = `Hello world!`;
s.startsWith(`Hello`) // true
s.endsWith(`!`) // true
s.includes(`o`) // true
這三個方法都支援第二個引數,表示開始搜尋的位置。
let s = `Hello world!`;
s.startsWith(`world`, 6) // true
s.endsWith(`Hello`, 5) // true
s.includes(`Hello`, 6) // false
8、repeat()repeat
方法返回一個新字串,表示將原字串重複n次。引數如果是小數,會被取整。如果repeat
的引數是負數或者Infinity
,會報錯。
`x`.repeat(3) // "xxx"
`na`.repeat(2.9) // "nana"
`na`.repeat(0) // ""
`na`.repeat(Infinity)
// RangeError
`na`.repeat(-1)
// RangeError
但是,如果引數是 0 到-1 之間的小數,則等同於 0,這是因為會先進行取整運算。0 到-1 之間的小數,取整以後等於-0,repeat
視同為 0。
`na`.repeat(-0.9) // ""
引數NaN
等同於 0。
`na`.repeat(NaN) // ""
如果repeat
的引數是字串,則會先轉換成數字。
`na`.repeat(`na`) // ""
`na`.repeat(`3`) // "nanana"
9、padStart(),padEnd()
如果某個字串不夠指定長度,會在頭部或尾部補全。padStart()
用於頭部補全,padEnd()
用於尾部補全。
`x`.padStart(5, `ab`) // `ababx`
`x`.padStart(4, `ab`) // `abax`
`x`.padEnd(5, `ab`) // `xabab`
`x`.padEnd(4, `ab`) // `xaba`
如果原字串的長度,等於或大於指定的最小長度,則返回原字串。
`xxx`.padStart(2, `ab`) // `xxx`
`xxx`.padEnd(2, `ab`) // `xxx`
如果省略第二個引數,預設使用空格補全長度。
`x`.padStart(4) // ` x`
`x`.padEnd(4) // `x `
padStart
的常見用途是為數值補全指定位數。下面程式碼生成 10 位的數值字串。
`1`.padStart(10, `0`) // "0000000001"
`12`.padStart(10, `0`) // "0000000012"
`123456`.padStart(10, `0`) // "0000123456"
10、模板字串
用反引號(`)標識,它可以當作普通字串使用,也可以用來定義多行字串,或者在字串中嵌入變數。
// 普通字串
`In JavaScript `
` is a line-feed.`
// 多行字串
`In JavaScript this is
not legal.`
console.log(`string text line 1
string text line 2`);
// 字串中嵌入變數
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
11、例項:模板編譯
let template = `
<ul>
<% for(let i=0; i < data.supplies.length; i++) { %>
<li><%= data.supplies[i] %></li>
<% } %>
</ul>
`;
上面程式碼在模板字串之中,放置了一個常規模板。該模板使用<%...%>
放置 JavaScript
程式碼,使用<%= ... %>
輸出 JavaScript
表示式。
怎麼編譯這個模板字串呢?
一種思路是將其轉換為 JavaScript 表示式字串。
echo(`<ul>`);
for(let i=0; i < data.supplies.length; i++) {
echo(`<li>`);
echo(data.supplies[i]);
echo(`</li>`);
};
echo(`</ul>`);
這個轉換使用正規表示式就行了。
let evalExpr = /<%=(.+?)%>/g;
let expr = /<%([sS]+?)%>/g;
template = template
.replace(evalExpr, ``);
echo( $1 );
echo(``)
.replace(expr, ``);
$1
echo(``);
template = `echo(`` + template + ``);`;
然後,將template封裝在一個函式裡面返回,就可以了。
let script =
`(function parse(data){
let output = "";
function echo(html){
output += html;
}
${ template }
return output;
})`;
return script;
將上面的內容拼裝成一個模板編譯函式compile。
function compile(template){
const evalExpr = /<%=(.+?)%>/g;
const expr = /<%([sS]+?)%>/g;
template = template
.replace(evalExpr, ``);
echo( $1 );
echo(``)
.replace(expr, ``);
$1
echo(``);
template = `echo(`` + template + ``);`;
let script =
`(function parse(data){
let output = "";
function echo(html){
output += html;
}
${ template }
return output;
})`;
return script;
}
compile函式的用法如下。
let parse = eval(compile(template));
div.innerHTML = parse({ supplies: [ "broom", "mop", "cleaner" ] });
// <ul>
// <li>broom</li>
// <li>mop</li>
// <li>cleaner</li>
// </ul>
11、String.raw()String.raw
方法,往往用來充當模板字串的處理函式,返回一個斜槓都被轉義(即斜槓前面再加一個斜槓)的字串,對應於替換變數後的模板字串。
String.raw`Hi
${2+3}!`;
// "Hi\n5!"
String.raw`Hiu000A!`;
// `Hi\u000A!`
如果原字串的斜槓已經轉義,那麼String.raw
不會做任何處理。
String.raw`Hi\n`
// "Hi\n"
String.raw
的程式碼基本如下。
String.raw = function (strings, ...values) {
let output = "";
let index;
for (index = 0; index < values.length; index++) {
output += strings.raw[index] + values[index];
}
output += strings.raw[index]
return output;
}
String.raw
方法可以作為處理模板字串的基本方法,它會將所有變數替換,而且對斜槓進行轉義,方便下一步作為字串來使用。
String.raw
方法也可以作為正常的函式使用。這時,它的第一個引數,應該是一個具有raw
屬性的物件,且raw
屬性的值應該是一個陣列。
String.raw({ raw: `test` }, 0, 1, 2);
// `t0e1s2t`
// 等同於
String.raw({ raw: [`t`,`e`,`s`,`t`] }, 0, 1, 2);