深入理解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) ,utf16引入了代理對,其規定用兩個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插入經過安全置換後的字符串的能力

模板字面量里不需要轉義單、雙引號,如果要使用反撇號則需要通過\來轉義。有變量可以使用${變量名}佔位(如果使用一個未定義的變量,總會拋出錯誤),模板字面量本身也是javascript表達式,所以你可以在一個模板字面量里嵌入另一個,如下

let name = "Nicholas",
message = `Hello ${
  `my name is ${name}`
}`;
console.log(message);

標籤函數的使用

function tag(literals,...substitutions){
  // 返回一個字符串
}
// 舉個栗子
let count = 10,
price = 0.25
message = passthru`${count}items cost $${count*price.toFixed(2)}.`

如果你有一個名為 passthru() 函數,那麼作為一個模板字面量標籤,它會接受3個參數:
首先是一個literals數組:相當於兩個變量位符把字符串切成了三段

  • 第一佔位符前面:空字符''
  • 第一個和第二個佔位符中間的items cost $
  • 第二個後面'.'

第二個參數就是count解釋的值,傳參為10,它也成為了substitutions數組的第一個元素,最後一個參數是count*price.toFixed(2)解釋的值2.5作為substitutions數組的第二個元素。substitutions的元素個數總是比literals的長度少1。

function passthru(literals,...substitutions){
  let result = '';
  // 根據substition的數量來確定循環的次數
  for(let i=0;i<substitutions.length;i++){
    result+=literals[i];
    result+=substitutions[i];
  }
  // 合併最後一個literal
  result+=literals[literals.length-1];
  return result;
}

String.raw()

模板標籤同樣可以訪問原生字符串信息,也就是說通過模板標籤可以訪問到字符轉義被轉換成等價字符前的原生字符串,最簡單的例子是使用內建的String.raw()標籤函數

let message1 = `Multiline\nstring`,
message2 = String.raw`Multiline\nstring`;
console.log(message1); // "Multiline
                       // string"
console.log(message2); // "Multiline\\nstring"

原生字符串信息同樣被傳入模板標籤,標籤函數的第一個參數是一個數組,它有一個額外的屬性raw,是一個包含每一個字面值的原生等價信息的數組。如literals[0]總有一個等價的literals.raw[0],它包含著它的原生字符串信息。

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

(0)
Walker的頭像Walker
上一篇 2026年3月10日 00:00
下一篇 2026年3月8日 15:40

相關推薦

  • Nuxt3_掃盲 入門與原理介紹【學習筆記】

    Nuxt 3 入門與原理介紹 💡 甚麼是 Nuxt 3? Nuxt 3 是基於 Vue 3 和 Vite 打造的全棧前端框架,支持: 服務端渲染(SSR) 靜態站點生成(SSG) 單頁應用(SPA) 構建全棧應用(支持 API) Nuxt 3 是 Vue 的“加強版”,幫你簡化項目結構和開發流程。 🔧 核心原理 功能 Nuxt 如何處理 ✅ 頁面路由 自動根…

    個人 2025年4月6日
    2.2K00
  • TS珠峰 002【學習筆記】

    泛型 /* * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git &a…

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

    node 的包管理機制和加載機制 npm search xxxnpm view xxxnpm install xxx nodejs 文件系統操作的 api Node.js 的 fs 模塊提供同步(Sync)與基於回調/Promise 的異步 API,可以操作本地文件與目錄。日常開發中常用的能力包括讀取、寫入、追加、刪除、遍歷目錄、監聽變化等。以下示例基於 C…

    個人 2025年11月24日
    30100
  • 深入理解ES6 005【學習筆記】

    解構:使用數據訪問更便捷 如果使用var、let或const解構聲明變量,則必須要提供初始化程序(也就是等號右側的值)如下會導致錯誤 // 語法錯誤 var {tyep,name} // 語法錯誤 let {type,name} // 語法錯誤 const {type,name} 使用解構給已經聲明的變量賦值,哪下 let node = { type:&qu…

    個人 2025年3月8日
    1.3K00
  • 【開篇】

    我是Walker,生於八十年代初,代碼與生活的旅者。全棧開發工程師,游走於前端與後端的邊界,執著於技術與藝術的交匯點。 代碼,是我編織夢想的語言;項目,是我刻畫未來的畫布。在鍵盤的敲擊聲中,我探索技術的無盡可能,讓靈感在代碼里永恆綻放。 深度咖啡愛好者,迷戀每一杯手衝的詩意與儀式感。在咖啡的醇香與苦澀中,尋找專注與靈感,亦如在開發的世界中追求極致與平衡。 騎…

    2025年2月6日 個人
    2.3K00
簡體中文 繁體中文 English