字符串和正則表達式
ES6 為我們增添幾個常用的字符串操作方法
-
includes() 方法。檢測指定文本,匹配到結果返回true,否則為false。可以用來替換indexOf來判斷字符串是否存在於另一個字符串中。
-
"abc".indexOf("a") > -1 => "abc".includes("a")
-
當需要獲取字段的索引值時,只能依靠indexOf(或lastIndexOf),匹配到的索引值為字符串第一個字符在 另一個字符串中的位置。
"abcd".indexOf("bc") //1
-
-
startsWith() 方法。在字符串的開始部分是否是以指定文本,是則返回true,反之為false。
-
endsWith() 方法。在字符串的結尾部分是否是以指定文本,是則返回true,反之為false。
-
上面兩個方法均可接受兩個參數。以startsWith為例。
-
"abc".startsWith('b', 1)
// true-
第一個參數為匹配文本
-
第二個參數設置檢測的起始位置
-
endsWith中,第二個參數設置起始位置為
字符長度 - 參數值
-
-
-
repeat() 方法。接收一個number類型參數,表示改字符串的重復次數。
-
"ab".repeat(2) // abab
-
若接收字符串可轉為數字。則進行隱式轉換。
-
若接收字符串不可轉為數字。則返回空。
-
新增3個字符編碼的方法。
JavaScript的字符編碼方式為 UTF-16 進行構建,即用 16個 0 / 1的組合表示一個字符。
-
編碼單元:這16個 0或1 的組合稱為一個編碼單元。
-
基本多文種平面:編碼單元的取值范圍稱為基本多文中平面。
- 基本多文種平面 -> Basic Multilingual Plane. (以下簡稱:BMP)
對於超出BMP的字符,16位編碼就無法表示了。為此UTF-16引入了代理對,用兩個16位編碼單元表示一個字符。
-
codePointAt() // 將字符轉換為字符編碼。
-
對於BMP字符集中的字符,與charCodeAt()效果相同。
"a".charCodeAt() // 97 "a".codePointAt() // 97
-
對於非BMP字符集中的字符,會展示完整的字符編碼。
-
"𠮷".charCodeAt() // 55362 "𠮷".charCodeAt(0) // 55362 "𠮷".charCodeAt(1) // 57271 "𠮷".codePointAt() // 134071 "𠮷".codePointAt(0) // 134071 "𠮷".codePointAt(1) // 57271 "𠮷".codePointAt(2) // undefined
-
根據以上的代碼,我們可以知道,ES 5 中的charCodeAt()方法,對於非BMP字符集中的字符,只截取代理對中的第一段16位編碼,第二段則默認忽略。
-
而ES 6新增的codePointAt()方法,很合理的使用了整個代理對來進行解析。且代理對的第二段作為輔助平面,而不做單獨解析。
-
-
關於檢測一個字符占用的編碼單元數量,以下函數可提供參考。
function is32Bit(c) { return c.codePointAt(0) > 0xFFFF; } console.log(is32Bit("𠮷")) // true console.log(is32Bit("a")) // false
-
-
String.fromCodePoint() // 將字符編碼轉換為字符
-
對於BMP字符集中的字符,與fromCharCode()效果相同。
-
String.fromCharCode(97) // a
-
String.fromCodePoint(97) // a
-
-
對於非BMP字符集中的字符,fromCodePoint能展示更完整的字符。
-
String.fromCharCode(9731, 9733, 9842, 0x2F804) // ☃★♲
-
String.fromCodePoint(9731, 9733, 9842, 0x2F804) // ☃★♲你
-
-
-
normalize() // 統一標准化編碼的表示形式,通常在國際化應用中使用較多
-
有很多歐洲國家的語言中有語調和重音符號。Unicode提供了兩種方法來進行編碼。
-
一種是直接帶重音符號的字符 例如:Ǒ (\u01D1)
-
-
另一種是合成符號:O (\u030C) 和 ̌(\u030C) 組成 Ǒ (\u01D1)
-
-
兩種方式變現的字符相同,但字符並不相同
'\u01D1'==='\u004F\u030C' //false
。ES 6新增的normalize(),能將Unicode正規化。可傳入四個可選值。-
NFC。默認參數,按照標准等價分解,然后在以標准等價方式進行重組。
-
NFD。按照標准等價分解。
-
NFD。按照兼容等價分解。
-
NFKD。按照兼容等價分解,然后在以兼容等價方式進行重組。
-
標准等價:視覺展示和語義上等價。
-
兼容等價:語義等價、視覺展示不等價。
-
-
在進行排序時,建議統一標准化進行處理。以下例子可供參考:
-
arr.sort(function(first, second) { let firstNormalized = first.normalize(), secondNormalized = second.normalize(); if (firstNormalized < secondNormalized) { return -1; } else if (firstNormalized === secondNormalized) { return 0; } else { return 1; } });
-
正則表達式的u修飾符
JavaScript的正則默認會將字符進行16位編碼處理,某些單個字符解析成兩個編碼。例如:"𠮷"
let text = "𠮷";
console.log(/^.$/.test(text)); // 匹配一個字符時得到 false
ES 6加如了 u修飾符,使編碼操作處理變為按字符處理。
let text = "𠮷";
console.log(/^.$/.test(text)); // 匹配一個字符時得到 false
console.log(/^.$/u.test(text)); // 匹配一個字符時得到 true
判斷瀏覽器JS解釋器是否支持正則中的 u修飾符,以下函數可提供參考:
function hasExpU () {
try {
var pattern = new RegExp(",", u);
return true;
} catch (ex) {
return false;
}
}
正則表達式的y修飾符
正則采用y修飾符后,匹配將會從lastIndex屬性值的位置為起點開始匹配。若從lastIndex開始的字符匹配到結果,則返回true,否則為false。換種說法,以lastIndex值為起點,進行startsWith匹配。
let text = "hello1 hello2 hello3",
pattern = /hello\d/y;
console.log(pattern.test(text)); // true
pattern.lastIndex = 1; // 初始化匹配起始位置
console.log(pattern.test(text)); // false
-
lastIndex = 1,即從"ello1 hello2 hello3"中匹配。正則中是以h開頭,與e不同,所以返回false。
-
當y修飾符的正則匹配到結果后,lastIndex會更新為匹配到結果的最后一個索引值。再次正則匹配操作,會進行疊加,稱為粘滯行為。
let text = "hello1 hello2 hello3",
pattern = /hello\d\s/y;
console.log(pattern.test(text)); // true
console.log(pattern.test(text)); // true
console.log(pattern.test(text)); // false (因為hello3后面無空格)
- 注意:只有調用正則對象的方法才會涉及粘滯行為。test、exec等。而字符串的方法不會觸發,如match。
正則表達式的復制
- 通過給RegExp構造函數傳遞第二個參數,可進行修飾符的覆蓋。
let re1 = /abc/i,
re2 = new RegExp(re1, "g");
console.log(re2); // /abc/g
正則的其他
- flags 可獲取正則表達式的修飾符。
let re1 = /abc/img;
console.log(re1.source); // abc (ES5 獲取正則表達式文本)
console.log(re1.flags); // img (ES6 獲取正則表達式修飾符)
模版字面量
模版字面量使用一對反引號表示( `` ),具有以下特點:
-
支持字符串拼接
-
支持換行
-
支持空格記錄 (換行情況下)
-
支持占位符 ( ${}, 且占位符中可以正常執行函數,表達式)
-
標簽函數
對於ES 6之前的字符串拼接, '+' 和 '' 雖然能夠完成拼接,但變量插入,換行、空格記錄等十分繁瑣。ES 6 提供的模版字面量提高了這一方面的開發效率。
let text = "test",
str = `This is
${text} sentence!` ;
console.log(str);
// This is
test sentence!
標簽函數:對模版字面量進一步處理的函數,放在反引號之前。
-
標簽函數接收兩個參數,literals 和 ...substitutions。
-
第一個參數是一個數組。以占位符為分割符號,分割元素組成的數組,累死split()函數分割后的返回值。
-
第二個參數為 占位符的集合,是一種類數組結構,有length屬性
let message = tag`hello world`; // tag 是我們自定義的一個標簽模版函數
function tag(literals, ...substitutions) {
// 內容
}
用法:模版處理、規避惡意腳本
- 模版處理。先看下面的例子,例子中str變量正確的獲取了字符串。
let a = 1, b = 2,
str = `${a} + ${b} = ${a + b}`;
console.log(str); // 1 + 2 = 3
現在需求變成 '10 + 20 = 30',模版變成:
str = `${a * 10} + ${b * 10} = ${a * 10 + b * 10}`; // 10 + 20 = 30
不免有些麻煩,模版較長時,維護起來並不容易。使用標簽函數試試。
const a = 1, b = 2;
const str = strWork`${a} + ${b} = ${a + b}`;
function strWork(literals, ...substitutions) {
let result = '';
for (let i = 0; i < substitutions.length; i++) {
result += literals[i];
result += String(Number(substitutions[i]) * 10);
}
return result;
}
console.log(str); // 10 + 20 = 30
- 規避惡意腳本
// 模板標簽過濾HTML字符串
function safeHTML(templateData) {
let s = templateData[0];
const args = arguments;
for (let i = 1, len = args.length; i < len; i++) {
let arg = String(args[i]); // 特殊字符的替換
s += arg.replace(/&/g, "&").replace(//g, ">");
s += templateData[i];
}
return s;
}
const str = ' <&&&&';
console.log(safeHTML`hello world ${str}`); // hello world <p> <&&&&
參考書籍:
《深入理解ES6》[美] NICHOLAS C. ZAKAS 著 劉振濤 譯