ES6 字符串的新增方法

2023-03-21 10:30 更新

1. String.fromCodePoint()

ES5 提供 String.fromCharCode() 方法,用于從 Unicode 碼點返回對應(yīng)字符,但是這個方法不能識別碼點大于 0xFFFF 的字符。

String.fromCharCode(0x20BB7)
// "?"

上面代碼中, String.fromCharCode() 不能識別大于 0xFFFF 的碼點,所以 0x20BB7 就發(fā)生了溢出,最高位 2 被舍棄了,最后返回碼點 U+0BB7 對應(yīng)的字符,而不是碼點 U+20BB7 對應(yīng)的字符。

ES6 提供了 String.fromCodePoint()方法,可以識別大于 0xFFFF 的字符,彌補(bǔ)了 String.fromCharCode() 方法的不足。在作用上,正好與下面的 codePointAt() 方法相反。

String.fromCodePoint(0x20BB7)
// "吉",注意,這個字符長得很像吉,但并不是漢字“吉”,為了展示出來以下這個符號用吉代替
String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y'
// true

上面代碼中,如果 String.fromCodePoint 方法有多個參數(shù),則它們會被合并成一個字符串返回。

注意, fromCodePoint 方法定義在 String 對象上,而 codePointAt 方法定義在字符串的實例對象上。

2. String.raw()

ES6 還為原生的 String 對象,提供了一個 raw()方法。該方法返回一個斜杠都被轉(zhuǎn)義(即斜杠前面再加一個斜杠)的字符串,往往用于模板字符串的處理方法。

String.raw`Hi\n${2+3}!`
// 實際返回 "Hi\\n5!",顯示的是轉(zhuǎn)義后的結(jié)果 "Hi\n5!"


String.raw`Hi\u000A!`;
// 實際返回 "Hi\\u000A!",顯示的是轉(zhuǎn)義后的結(jié)果 "Hi\u000A!"

如果原字符串的斜杠已經(jīng)轉(zhuǎn)義,那么 String.raw() 會進(jìn)行再次轉(zhuǎn)義。

String.raw`Hi\\n`
// 返回 "Hi\\\\n"


String.raw`Hi\\n` === "Hi\\\\n" // true

String.raw() 方法可以作為處理模板字符串的基本方法,它會將所有變量替換,而且對斜杠進(jìn)行轉(zhuǎn)義,方便下一步作為字符串來使用。

String.raw() 本質(zhì)上是一個正常的函數(shù),只是專用于模板字符串的標(biāo)簽函數(shù)。如果寫成正常函數(shù)的形式,它的第一個參數(shù),應(yīng)該是一個具有 raw 屬性的對象,且 raw 屬性的值應(yīng)該是一個數(shù)組,對應(yīng)模板字符串解析后的值。

// `foo${1 + 2}bar`
// 等同于
String.raw({ raw: ['foo', 'bar'] }, 1 + 2) // "foo3bar"

上面代碼中, String.raw() 方法的第一個參數(shù)是一個對象,它的 raw 屬性等同于原始的模板字符串解析后得到的數(shù)組。

作為函數(shù), String.raw() 的代碼實現(xiàn)基本如下。

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;
}

3. 實例方法:codePointAt()

JavaScript 內(nèi)部,字符以UTF-16 的格式儲存,每個字符固定為 2 個字節(jié)。對于那些需要 4 個字節(jié)儲存的字符(Unicode 碼點大于 0xFFFF 的字符),JavaScript 會認(rèn)為它們是兩個字符。

var s = "吉";


s.length // 2
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271

上面代碼中,漢字“吉”(注意,這個字不是“吉祥”的“吉”)的碼點是 0x20BB7 ,UTF-16 編碼為 0xD842 0xDFB7 (十進(jìn)制為 55362 57271 ),需要 4 個字節(jié)儲存。對于這種 4 個字節(jié)的字符,JavaScript 不能正確處理,字符串長度會誤判為 2 ,而且 charAt() 方法無法讀取整個字符, charCodeAt() 方法只能分別返回前兩個字節(jié)和后兩個字節(jié)的值。

ES6 提供了 codePointAt() 方法,能夠正確處理 4 個字節(jié)儲存的字符,返回一個字符的碼點。

let s = '吉a';


s.codePointAt(0) // 134071
s.codePointAt(1) // 57271


s.codePointAt(2) // 97

codePointAt() 方法的參數(shù),是字符在字符串中的位置(從 0 開始)。上面代碼中,JavaScript 將“吉a”視為三個字符,codePointAt 方法在第一個字符上,正確地識別了“吉”,返回了它的十進(jìn)制碼點 134071(即十六進(jìn)制的 20BB7 )。在第二個字符(即“吉”的后兩個字節(jié))和第三個字符“a”上, codePointAt() 方法的結(jié)果與 charCodeAt() 方法相同。

總之, codePointAt() 方法會正確返回 32 位的 UTF-16 字符的碼點。對于那些兩個字節(jié)儲存的常規(guī)字符,它的返回結(jié)果與 charCodeAt() 方法相同。

codePointAt() 方法返回的是碼點的十進(jìn)制值,如果想要十六進(jìn)制的值,可以使用 toString() 方法轉(zhuǎn)換一下。

let s = '吉a';


s.codePointAt(0).toString(16) // "20bb7"
s.codePointAt(2).toString(16) // "61"

你可能注意到了, codePointAt() 方法的參數(shù),仍然是不正確的。比如,上面代碼中,字符 a 在字符串 s 的正確位置序號應(yīng)該是 1,但是必須向 codePointAt() 方法傳入 2。解決這個問題的一個辦法是使用 for...of 循環(huán),因為它會正確識別 32 位的 UTF-16 字符。

let s = '吉a';
for (let ch of s) {
  console.log(ch.codePointAt(0).toString(16));
}
// 20bb7
// 61

另一種方法也可以,使用擴(kuò)展運(yùn)算符( ... )進(jìn)行展開運(yùn)算。

let arr = [...'吉a']; // arr.length === 2
arr.forEach(
  ch => console.log(ch.codePointAt(0).toString(16))
);
// 20bb7
// 61

codePointAt() 方法是測試一個字符由兩個字節(jié)還是由四個字節(jié)組成的最簡單方法。

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


is32Bit("吉") // true
is32Bit("a") // false

4. 實例方法:normalize()

許多歐洲語言有語調(diào)符號和重音符號。為了表示它們,Unicode 提供了兩種方法。一種是直接提供帶重音符號的字符,比如 ǒ (\u01D1)。另一種是提供合成符號(combining character),即原字符與重音符號的合成,兩個字符合成一個字符,比如 O (\u004F)和 ˇ (\u030C)合成 ǒ (\u004F\u030C)。

這兩種表示方法,在視覺和語義上都等價,但是 JavaScript 不能識別。

'\u01D1'==='\u004F\u030C' //false


'\u01D1'.length // 1
'\u004F\u030C'.length // 2

上面代碼表示,JavaScript 將合成字符視為兩個字符,導(dǎo)致兩種表示方法不相等。

ES6 提供字符串實例的 normalize()方法,用來將字符的不同表示方法統(tǒng)一為同樣的形式,這稱為 Unicode 正規(guī)化。

'\u01D1'.normalize() === '\u004F\u030C'.normalize()
// true

normalize 方法可以接受一個參數(shù)來指定 normalize 的方式,參數(shù)的四個可選值如下。

  • NFC ,默認(rèn)參數(shù),表示“標(biāo)準(zhǔn)等價合成”(Normalization Form Canonical Composition),返回多個簡單字符的合成字符。所謂“標(biāo)準(zhǔn)等價”指的是視覺和語義上的等價。
  • NFD ,表示“標(biāo)準(zhǔn)等價分解”(Normalization Form Canonical Decomposition),即在標(biāo)準(zhǔn)等價的前提下,返回合成字符分解的多個簡單字符。
  • NFKC ,表示“兼容等價合成”(Normalization Form Compatibility Composition),返回合成字符。所謂“兼容等價”指的是語義上存在等價,但視覺上不等價,比如“囍”和“喜喜”。(這只是用來舉例, normalize 方法不能識別中文。)
  • NFKD ,表示“兼容等價分解”(Normalization Form Compatibility Decomposition),即在兼容等價的前提下,返回合成字符分解的多個簡單字符。

'\u004F\u030C'.normalize('NFC').length // 1
'\u004F\u030C'.normalize('NFD').length // 2

上面代碼表示, NFC 參數(shù)返回字符的合成形式, NFD 參數(shù)返回字符的分解形式。

不過, normalize 方法目前不能識別三個或三個以上字符的合成。這種情況下,還是只能使用正則表達(dá)式,通過 Unicode 編號區(qū)間判斷。

5. 實例方法:includes(), startsWith(), endsWith()

傳統(tǒng)上,JavaScript 只有 indexOf 方法,可以用來確定一個字符串是否包含在另一個字符串中。ES6 又提供了三種新方法。

  • includes():返回布爾值,表示是否找到了參數(shù)字符串。
  • startsWith():返回布爾值,表示參數(shù)字符串是否在原字符串的頭部。
  • endsWith():返回布爾值,表示參數(shù)字符串是否在原字符串的尾部。

let s = 'Hello world!';


s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true

這三個方法都支持第二個參數(shù),表示開始搜索的位置。

let s = 'Hello world!';


s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false

上面代碼表示,使用第二個參數(shù) n 時, endsWith 的行為與其他兩個方法有所不同。它針對前 n 個字符,而其他兩個方法針對從第 n 個位置直到字符串結(jié)束。

6. 實例方法:repeat()

repeat 方法返回一個新字符串,表示將原字符串重復(fù) n 次。

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""

參數(shù)如果是小數(shù),會被取整。

'na'.repeat(2.9) // "nana"

如果 repeat 的參數(shù)是負(fù)數(shù)或者 Infinity ,會報錯。

'na'.repeat(Infinity)
// RangeError
'na'.repeat(-1)
// RangeError

但是,如果參數(shù)是 0 到-1 之間的小數(shù),則等同于 0,這是因為會先進(jìn)行取整運(yùn)算。0 到-1 之間的小數(shù),取整以后等于 -0 , repeat 視同為 0。

'na'.repeat(-0.9) // ""

參數(shù) NaN 等同于 0。

'na'.repeat(NaN) // ""

如果 repeat 的參數(shù)是字符串,則會先轉(zhuǎn)換成數(shù)字。

'na'.repeat('na') // ""
'na'.repeat('3') // "nanana"

7. 實例方法:padStart(),padEnd()

ES2017 引入了字符串補(bǔ)全長度的功能。如果某個字符串不夠指定長度,會在頭部或尾部補(bǔ)全。 padStart() 用于頭部補(bǔ)全, padEnd() 用于尾部補(bǔ)全。

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'


'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'

上面代碼中, padStart() 和 padEnd() 一共接受兩個參數(shù),第一個參數(shù)是字符串補(bǔ)全生效的最大長度,第二個參數(shù)是用來補(bǔ)全的字符串。

如果原字符串的長度,等于或大于最大長度,則字符串補(bǔ)全不生效,返回原字符串。

'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'

如果用來補(bǔ)全的字符串與原字符串,兩者的長度之和超過了最大長度,則會截去超出位數(shù)的補(bǔ)全字符串。

'abc'.padStart(10, '0123456789')
// '0123456abc'

如果省略第二個參數(shù),默認(rèn)使用空格補(bǔ)全長度。

'x'.padStart(4) // '   x'
'x'.padEnd(4) // 'x   '

padStart() 的常見用途是為數(shù)值補(bǔ)全指定位數(shù)。下面代碼生成 10 位的數(shù)值字符串。

'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"

另一個用途是提示字符串格式。

'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"

8. 實例方法:trimStart(),trimEnd()

ES2019 對字符串實例新增了 trimStart()trimEnd() 這兩個方法。它們的行為與trim() 一致, trimStart()消除字符串頭部的空格,trimEnd() 消除尾部的空格。它們返回的都是新字符串,不會修改原始字符串。

const s = '  abc  ';


s.trim() // "abc"
s.trimStart() // "abc  "
s.trimEnd() // "  abc"

上面代碼中, trimStart() 只消除頭部的空格,保留尾部的空格。 trimEnd() 也是類似行為。

除了空格鍵,這兩個方法對字符串頭部(或尾部)的 tab 鍵、換行符等不可見的空白符號也有效。

瀏覽器還部署了額外的兩個方法, trimLeft() 是 trimStart() 的別名, trimRight() 是 trimEnd() 的別名。

9. 實例方法:matchAll()

matchAll()方法返回一個正則表達(dá)式在當(dāng)前字符串的所有匹配,詳見《正則的擴(kuò)展》的一章。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號