深入理解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://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日
    35800
  • 從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日
    27300
簡體中文 繁體中文 English