深入理解ES6 002【學習筆記】

字串與正規表達式

字串與正規表達式

Javascript 字串一直基於 16 位元字元編碼(UTF-16)進行建構。每 16 位元的序列是一個編碼單元(code unit),代表一個字元。length、charAt() 等字串屬性與方法都基於這個編碼單元構造的。
Unicode 的目標是為世界上每一個字元提供全球唯一的識別符。如果我們把字元長度限制在 16 位元,碼位數量將不足以表示如此多的字元。「全球唯一的識別符」,又稱作碼位(code point),是從 0 開始的數值。而表示字元的這些數值或碼位,我們稱之為字元編碼 (character encode)。字元編碼必須將碼位編碼為內部一致的編碼單元。對於 UTF-16 來說,碼位可以由多種編碼單元表示。

更好的 Unicode 支援,在過去 16 位元足以包含任何字元 (每 16 位元的序列是一個編碼單元,代表一個字元,在過去 16 位元足以包含任何字元),直到Unicode引入擴展字元集,編碼規則才不得不進行變更。

UTF-16

前 $2^{16}$ 個碼位均以 16 位元的編碼單元表示,這個範圍被稱作基本多文種平面 BMP (Basic Multilingual Plan)。超出這個範圍的碼位則要歸屬於某個輔助平面(supplementary plane),UTF-16 引入了代理對,其規定用兩個 16 位元編碼單元表示一個碼位。這也就是說,字串裡的字元有兩種,一種是由一個編碼單元16表示的 BMP 字元,另一種是由兩個編碼單元32位元表示的輔助平面字元,如字元:‘𠮷’ (String.fromCodePoint(134071))

ECMAScript 5中,所有字串的操作都基於 16 位元編碼單元。如果採用同樣的方式處理包含代理對的UTF-16編碼字元,得到的結果可能與預期不符

let text = "𠮷";
console.log(text.length); //2
console.log(/^.$/.test(text)); //false
console.log(text.charAt(0)); // "" 
console.log(text.charAt(1)); // ""
console.log(text.charCodeAt(0)); // 55362
console.log(text.charCodeAt(1)); // 57271
  • 變數 text 的長度事實上是 1,但它的 length 屬性卻是 2
  • 變數 text 被判定為兩個字元,因此匹配單一字元的正規表達式失效
  • 前後兩個 16 位元的編碼單元都不表示任何可列印的字元,因此charAt()方法不會返回合法字串
  • charCodeAt() 同樣不能正確地辨識字元。它會返回每個 16 位元編碼單元對應的數值

codePointAt()方法

對於 BMP 字元集中的字元,codePointAt()方法的返回值與chartCodeAt()方法相同,而對於非 BMP 字元集來說返回值則不同。字串‘𠮷a’第一個字元是非 BMP 的,包含兩個編碼單元,所以它的 length 屬性值為 3。ES6 完全支援UTF-16codePointAt()方法,這個方法接受編碼單元的位置而非字元位置作為參數,返回與字串中給定位置對應的碼位,即一個整數值。

let text = '𠮷a'
console.log(text.length)

console.log(text.charCodeAt(0)) // 55362
console.log(text.charCodeAt(1)) // 57271
console.log(text.charCodeAt(2)) // 97

console.log(text.codePointAt(0)) // 134071
console.log(text.codePointAt(1)) // 57271
console.log(text.codePointAt(2)) // 97

在偵測一個字元佔用的編碼單元,可以寫如下的函式來偵測

function is32Bite(c){
  return c.codePointAt(0)>0xFFFF;
}

console.log(is32Bite('𠮷')) // true
console.log(is32Bite('a')) // false

fromCodePoint()方法

透過一個字元的碼位返回一個字元,可以看成是String.fromCharCode()的擴展版。對於 BMP 的所有字元,兩個方法的執行結果相同。只有傳遞非 BMP 的碼位作為參數時,二者的執行結果才有可能不同。

console.log(String.fromCodePoint(134071)) // 𠮷

normalize()方法

Unicode 的另一個有趣之處是,如果我們要對不同字元進行排序或比較操作,會存在一種可能,它們是等效的。代表相同文字的字元可能存在的碼位不同。所以做比較時要使用normalize()方法來先標準化一下

只要牢記,在對比字串之前,一定先把它們標籤化為同一個形式。

let normalized  = values.map(funciton(text){
  return text.normalize();
});

normalized.sort(funciton(first,second){
  if(first < second){
    return -1;
  } else if (first === second) {
    return 0;
  } else {
    return 1;
  }
})

正規表達式 u 修飾符

一個支援Unicode的修飾符u 使它從編碼單元操作模式切換成為字元模式,如此一來正規表達式就不會將代理對視為兩個字元,從而完全按照預期正常執行。如:

let text = '𠮷a'

console.log(text.length)
console.log(/^.$/.test(text)) //false
console.log(/^.$/u.test(text)) //true 使用了u修飾符後,正規表達式會匹配字元,從而就可以匹配日文文字字元

計算碼位數量

ES6 仍然不支援字串碼位數量偵測(length 仍然返回字串編碼單元的數量),但有了u修飾符後,你就可以透過正規表達式來解決這個問題。

// 長字串可能會有效率問題,可以使用字串迭代器來處理
function codePointLength(text){
  let rs = text.match(/[\s\S]/gu);
  return rs?rs.length:0
}
// 判斷瀏覽器是否支援 u
function hasRegExU(){
  try{
    var partten = new RegExp(".","u");
     return true
  }catch(ex){
    return false
  }
}

字串的子字串辨識

  • trim()
  • includes() 如果字串的起始部分偵測到指定文字則返回 true,否則返回 false
  • startWith()如果字元的起始部分偵測到指定文字則返回 true,否則返回 false
  • endsWith() 如果字串的結束部分偵測到指定文字則返回 true,否則返回 false
  • repeat() 返回當前字串重複一定次數的新字串

兩個參數,第一個指定要搜尋的文字,第二個參數是可選的,指定搜尋位置的索引位置,如果你需要在一個字串中尋找一個子字串的實際位置,還是需要使用indexOf()lastIndexOf()方法

repeat()方法

ES6 還增加了一個repeat(),它接受一個 number 型別的參數,表示該字串的重複次數,返回值是當前字串重複一定次數後的新字串。例如在程式碼格式化工具中建立縮排層級

let indent = " ".repeat(4),
indentLevel = 0;
// 當需要增加縮排時
let newIndent = indent.repeat(++indentLevel)

正規表達式中的 y 修飾符

它會影響正規表達式搜尋過程中的 sticky 屬性,當在字串中開始字元匹配時,它會通知搜尋從正規表達式的 lastIndex 屬性開始進行。如果在指定位置沒有成功匹配,則停止繼續匹配。只有呼叫 exec() 和 test() 這些正規表達式物件的方法時才會涉及 lastIndex 屬性

正規表達式的複製

var reg1 = /ab/i,
// ES5 中拋出例外,ES6 中正常執行
reg2 = new RegExp(reg1,"g")

let re = /ab/g
console.log(re.source); // "ab"
console.log(re.flags); // "g"

樣板字面值

  • 多行字串:一個正式的多行字串概念
  • 基本的字串格式化:將變數的值嵌入字串的能力
  • HTML 跳脫:向 HTML 插入經過安全置換後的字串的能力

樣板字面值裡不需要跳脫單引號、雙引號,如果要使用反引號則需要透過 \ 來

主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://www.walker-learn.xyz/archives/4309

(0)
Walker的頭像Walker
上一篇 2025年3月8日 10:59
下一篇 2025年3月8日 02:11

相關推薦

  • 深入理解ES6 003【學習筆記】

    函數參數預設值,以及一些關於 arguments 物件,如何使用運算式作為參數、參數的暫時性死區。 以前設定預設值總是利用在含有邏輯或運算子的運算式中,前一個值是 false 時,總是回傳後面那個值,但如果我們給參數傳入 0 時,就會有些麻煩。 需要去驗證一下型別 function makeRequest(url,timeout,callback){ timeout = t…

    個人 2025年3月8日
    1.3K00
  • Node深入淺出(聖思園教育) 001【學習筆記】

    node 從非同步程式設計典範理解 Node.js Node.js 的定位與核心思想 基於 V8 引擎 + libuv 事件驅動函式庫,將 JavaScript 從瀏覽器帶到伺服器端。 採用單執行緒事件迴圈處理 I/O,最大化利用 CPU 等待 I/O 的時間切片,特別適合高併發、I/O 密集型情境。 「不要阻塞主執行緒」是設計哲學:盡量把耗時操作交給核心或執行緒池,回呼結果…

    個人 2025年11月24日
    35900
  • 從0到1落地微前端架構 001【學習筆記】

    微前端 js 隔離css 隔離元素隔離生命週期預載入資料通訊應用程式跳轉多層巢狀 說明 使用的是 Mermaid 的 flowchart 語法,Markdown 渲染器如 Typora、VitePress、一些 Git 平台都支援。 保留了: 基座應用 main-vue3 各子應用:child-nuxt2-home、child-vue2-job、child-vu…

    2025年4月20日
    1.6K00
  • 深入理解ES6 008【學習筆記】

    迭代器(Iterator)和產生器(Generator)這項新特性對於高效的資料處理而言是不可或缺的,你也會發現在語言的其他特性中也都有迭代器的蹤影:新的 for-of 迴圈、展開運算子 (...)、甚至連非同步程式設計都可以使用迭代器。 迭代器是一種特殊的物件,它具有一些專門為迭代過程設計的專有介面,所有的迭代器物件都有一個 next() 方法,每次呼叫都傳回一個結果對…

    個人 2025年3月8日
    1.2K00
  • Go工程師體系課 003【學習筆記】

    grpc grpc grpc-go grpc 無縫整合了 protobuf protobuf 習慣用 Json、XML 資料儲存格式的你們,相信大多都沒聽過 Protocol Buffer。 Protocol Buffer 其實是 Google 出品的一種輕量且高效的結構化資料儲存格式,效能比 Json、XML 真的強!太!多! protobuf…

    個人 2025年11月25日
    27400
簡體中文 繁體中文 English