ES6-02 字符串與正則表達式

ES6學習筆記-字符串與正則表達式

  • JS字符串編碼

在ES6之前,JS的字符串以16位字符編碼(UCS-2)為基礎(chǔ)。

每個 16 位序列都是一個碼元( code unit ),用于表示一個字符。

字符串所有的屬性與方法都是基于 16 位的碼元。

原本16 位足以容納任何字符,由于Unicode 引入了擴展字符集,那么就不夠用了。

UTF-16 是變長的字符編碼方式,有 16 位與 32 位兩種情況。 JS 原先使用的則是固定 16 位(雙字節(jié))的字符編碼方式,即 UCS-2 。

Unicode目標是給世界上所有的字符提供全局唯一標識符,而 16 位的字符長度限制已不能滿足這種需求。
這些全球唯一標識符被稱為代碼點( code points ),是從 0 開始的簡單數(shù)字。
代碼點是如你想象的字符代碼那樣,用一個數(shù)字來代表一個字符
字符編碼要求將代碼點轉(zhuǎn)換為內(nèi)部一致的碼元,而對于 UTF-16 來說,代碼點可以由多個碼元組成

在 UTF-16 中的第一個 2^16 代碼點表示單個 16 位碼元,這個范圍被稱為多語言基本平面(
Basic Multilingual Plane , BMP )。
任何超出該范圍的代碼點都不能用單個 16 位碼元表示,而是會落在擴展平面( supplementary planes )內(nèi)。
UTF-16 引入了代理對(surrogate pairs )來解決這個問題,允許使用兩個 16 位碼元來表示單個代碼點。
這意味著字符串內(nèi)的任意單個字符都可以用一個碼元(共 16 位)或兩個碼元(共 32 位)來表示,
前者對應基本平面字符,而后者對應擴展平面字符。

在ES5中,所有字符串操作都基于16位碼元,那么在處理包含代理對的 UTF-16 字符時會出現(xiàn)預期外的結(jié)果。

var text = "??" ;// 使用了代理對,“??”當作兩個16位字符對待。
console.log(text.length); // 2,而不是1
console.log(/^.$/.test(text)); // false,匹配失敗,這里有兩個字符。 
console.log(text.charAt(0)); // "" ,16位代碼點都不是可打印字符。
console.log(text.charAt(1)); // ""
console.log(text.charCodeAt(0)); // 55362 // 返回每個碼元的16位數(shù)字。
console.log(text.charCodeAt(1)); // 57271
  • codePointAt() 方法

ES6為全面支持 UTF-16 而新增的方法,可以在給定字符串中按位置提取 Unicode 代碼點。
該方法接受的是碼元位置而非字符位置,并返回一個整數(shù)值。

var text = "??a";
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

操作對象是 BMP 字符,則codePointAt()與charCodeAt()返回值相同。
text 字符串的第一個字符不是 BMP 字符,因此它占用了兩個碼元,字符串的length 屬性是 3 而不是 2 。
charCodeAt() 方法只返回了位置 0 的第一個碼元;而codePointAt() 返回的是完整的代碼點,即使它占用了多個碼元。

判斷字符包含了一個還是兩個碼元

function is32Bit(c) {
    return c.codePointAt(0) > 0xFFFF;
}
console.log(is32Bit("")); // true
console.log(is32Bit("a")); // false

16 位字符的上邊界用十六進制表示就是 FFFF ,因此任何大于該數(shù)字的代碼點必須用兩個碼元(共 32 位)來表示。

  • String.fromCodePoint() 方法

codePointAt()來提取字符串內(nèi)中某個字符的代碼點,String.fromCodePoint()用給定的代碼點來產(chǎn)生包含單個字符的字符串。

console.log(String.fromCodePoint(134071)); // "??"

可以將 String.fromCodePoint() 視為 String.fromCharCode() 的完善版本。兩者處理 BMP 字
符時會返回相同結(jié)果,只有處理 BMP 范圍之外的字符時才會有差異。

  • normalize() 方法

ES6 給字符串提供了 normalize() 方法,以支持 Unicode 標準形式。

Unicode 標準形式:
Normalization Form Canonical Composition ( "NFC" ),這是默認值;
Normalization Form Canonical Decomposition ( "NFD" );
Normalization Form Compatibility Composition ( "NFKC" );
Normalization Form Compatibility Decomposition ( "NFKD" )。

當比較字符串時,它們必須被標準化為同一種形式。

// 將values數(shù)組中的字符串轉(zhuǎn)換為一種標準形式,以便讓轉(zhuǎn)換后的數(shù)組可以被正確排序。
var normalized = values.map(function (text) {
    return text.normalize();
});
normalized.sort(function (first, second) {
    if (first < second) {
        return -1;
    } else if (first === second) {
        return 0;
    } else {
        return 1;
    }
});
// 比較過程中調(diào)用 normalize() 來對原始數(shù)組進行排序。    
values.sort(function (first, second) {
    var firstNormalized = first.normalize(),
        secondNormalized = second.normalize();
    if (firstNormalized < secondNormalized) {
        return -1;
    } else if (firstNormalized === secondNormalized) {
        return 0;
    } else {
        return 1;
    }
});

first 與 second 再一次使用同一方式被標準化了。

  • 正則表達式 u 標志

正則表達式假定單個字符使用一個 16 位的碼元來表示。
為了解決這個問題,ES6為正則表達式定義了用于處理Unicode的u標志。

u 標志如何運作:

當一個正則表達式設(shè)置了u標志時,它的工作模式將切換到針對字符,而不是針對碼元。
這意味著正則表達式將不會被字符串中的代理對所混淆,而是會如預期那樣工作。

var text = "??";
console.log(text.length); // 2
console.log(/^.$/.test(text)); // false,不使用 u 標志時,該正則表達式只匹配碼元。
console.log(/^.$/u.test(text)); // true,啟用 u 標志后,正則表達式就會比較字符而不是碼元。

計算代碼點數(shù)量

ES6 并沒有添加方法用于判斷一個字符串包含多少個代碼點。

可借助 u 標志,使用正則表達式來進行計算。

function codePointLength(text) {
    var result = text.match(/[\s\S]/gu);
    return result ? result.length : 0;
}
console.log(codePointLength("abc")); // 3
console.log(codePointLength("??bc")); // 3
/*此例調(diào)用了 match() 方法來檢查 text 中的空白字符與非空白字符(使用 [\s\S] 以確保
該模式能匹配換行符),所用的正則表達式啟用了全局與 Unicode 特性。在匹配至少成功一
次的情況下, result 變量會是包含匹配結(jié)果的數(shù)組,因此該數(shù)組的長度就是字符串中代碼
點的數(shù)量。在 Unicode 中,字符串 "abc" 與 "bc" 同樣包含三個字符,所以數(shù)組長度為 3
。
雖然這種方法可用,但它并不快,尤其在操作長字符串時。你也可以使用字符串的迭代
器來達到相同目的。一般來說,只要有可能就應盡量減少對代碼點數(shù)量的計算。
*/

判斷是否支持 u 標志

使用一個函數(shù)來判斷是否支持 u 標志是最安全的方式

function hasRegExpU() {
    try {
        var pattern = new RegExp(".", "u");
        return true;
    } catch (ex) {
        return false;
    }
}
/*
若你的代碼仍然需要在舊版 JS 引擎中工作,那么在使用 u 標志時應當始終使用
RegExp 構(gòu)造器。這會防止語法錯誤,并允許你有選擇地檢測并使用 u 標志,而不會導
致執(zhí)行被中斷。
*/
  • 識別子字符串的方法

includes()方法,在給定文本存在于字符串中的任意位置時會返回true,否則返回false;
startsWith()方法,在給定文本出現(xiàn)在字符串起始處時返回true,否則返回false;
endsWith()方法,在給定文本出現(xiàn)在字符串結(jié)尾處時返回true ,否則返回false。

每個方法都接受兩個參數(shù):需要搜索的文本,以及可選的搜索起始位置索引。
當提供了第二個參數(shù)時, includes() 與 startsWith() 方法會從該索引位置開始嘗試匹配;
而endsWith() 方法會將字符串長度減去該參數(shù),以此為起點開始嘗試匹配。
當?shù)诙€參數(shù)未提供時, includes() 與 startsWith() 方法會從字符串起始處開始查找,
而 endsWith() 方法則從尾部開始。實際上,第二個參數(shù)減少了搜索字符串的次數(shù)。

var msg = "Hello world!";
console.log(msg.startsWith("Hello")); // true
console.log(msg.endsWith("!")); // true
console.log(msg.includes("o")); // true
console.log(msg.startsWith("o")); // false
console.log(msg.endsWith("world!")); // true
console.log(msg.includes("x")); // false
console.log(msg.startsWith("o", 4)); // true
console.log(msg.endsWith("o", 8)); // true
console.log(msg.includes("o", 8)); // false

若要找到它們在另一個字符串中的確切位置,則需要使用 indexOf() 和 lastIndexOf()。

如果向 startsWith() 、 endsWith() 或 includes() 方法傳入了正則表達式而不是字符
串,會拋出錯誤。這與 indexOf() 以及 lastIndexOf() 方法的表現(xiàn)形成了反差,它們會
將正則表達式轉(zhuǎn)換為字符串并搜索它。

  • repeat() 方法

repeat() 方法,接受一個參數(shù)作為字符串的重復次數(shù),返回一個將初始字符串重復指定次數(shù)的新字符串。

console.log("x".repeat(3)); // "xxx"
console.log("hello".repeat(2)); // "hellohello"
console.log("abc".repeat(4)); // "abcabcabcabc"
  • 正則表達式 y 標志

在Firefox實現(xiàn)了對正則表達式y(tǒng)標志的專有擴展之后,ES6 將該實現(xiàn)標準化。
y標志影響正則表達式搜索時的粘連( sticky )屬性,它表示從正則表達式的 lastIndex 屬性值的
位置開始檢索字符串中的匹配字符。如果在該位置沒有匹配成功,那么正則表達式將停止檢
索。

var text = "hello1 hello2 hello3",
    pattern = /hello\d\s?/,
    result = pattern.exec(text),
    globalPattern = /hello\d\s?/g,
    globalResult = globalPattern.exec(text),
    stickyPattern = /hello\d\s?/y,
    stickyResult = stickyPattern.exec(text);
console.log(result[0]); // "hello1 "
console.log(globalResult[0]); // "hello1 "
console.log(stickyResult[0]); // "hello1 "

// 表示三個模式的正則表達式都應當從第二個字符開始嘗試匹配。
pattern.lastIndex = 1;
globalPattern.lastIndex = 1;
stickyPattern.lastIndex = 1;

result = pattern.exec(text);//不使用任何標志的正則表達式完全忽略了對于lastIndex的更改
globalResult = globalPattern.exec(text);//而使用g標志的正則表達式繼續(xù)匹配了 "hello2 "
stickyResult = stickyPattern.exec(text);//粘連的正則表達式則在第二個字符處沒有匹配成功
console.log(result[0]); // "hello1 "
console.log(globalResult[0]); // "hello2 "
console.log(stickyResult[0]); // Error! stickyResult is null

一旦匹配操作成功,粘連標志就會將匹配結(jié)果之后的那個字符的索引值保存在 lastIndex
中;若匹配未成功,那么 lastIndex 的值將重置為 0 。全局標志的行為與其相同。

var text = "hello1 hello2 hello3",
    pattern = /hello\d\s?/,
    result = pattern.exec(text),
    globalPattern = /hello\d\s?/g,
    globalResult = globalPattern.exec(text),
    stickyPattern = /hello\d\s?/y,
    stickyResult = stickyPattern.exec(text);
console.log(result[0]); // "hello1 "
console.log(globalResult[0]); // "hello1 "
console.log(stickyResult[0]); // "hello1 "
console.log(pattern.lastIndex); // 0
console.log(globalPattern.lastIndex); // 7
console.log(stickyPattern.lastIndex); // 7
result = pattern.exec(text);
globalResult = globalPattern.exec(text);
stickyResult = stickyPattern.exec(text);
console.log(result[0]); // "hello1 "
console.log(globalResult[0]); // "hello2 "
console.log(stickyResult[0]); // "hello2 "
console.log(pattern.lastIndex); // 0
console.log(globalPattern.lastIndex); // 14
console.log(stickyPattern.lastIndex); // 14

對于 stickyPattern 和 globalPattern 模式變量來說,第一次調(diào)用之后 lastIndex 的值均
被更改為 7 ,而第二次則均被改為 14。

  1. 只有調(diào)用正則表達式對象上的方法(例如 exec() 與 test() 方法), lastIndex 屬性
    才會生效。而將正則表達式作為參數(shù)傳遞給字符串上的方法(例如 match() ),并不會
    體現(xiàn)粘連特性。
  2. 當使用 ^ 字符來匹配字符串的起始處時,粘連的正則表達式只會匹配字符串的起始處
    (或者在多行模式下匹配行首)。當 lastIndex 為 0 時, ^ 不會讓粘連的正則表達式
    與非粘連的有任何區(qū)別;而當 lastIndex 在單行模式下不對應整個字符串起始處,或者
    當它在多行模式下不對應行首時,粘連的正則表達式永遠不會匹配成功。

根據(jù)sticky屬性來檢測 y 標志是否存在

var pattern = /hello\d/y;
console.log(pattern.sticky);
// sticky屬性由y標志存在與否決定,是只讀的,它的值不能在代碼中修改。

檢測JS 引擎是否支持y標志

function hasRegExpY() {
    try {
        var pattern = new RegExp(".", "y");
        return true;
    } catch (ex) {
        return false;
    }
}
/*
在舊版JS引擎中運行的代碼中使用y標志,請確保使用 RegExp 構(gòu)造器來定義正則表達式,以避免語法錯誤。
*/
  • 復制正則表達式

ES5中可以將正則表達式傳遞給RegExp構(gòu)造器來復制它

var re1 = /ab/i,
re2 = new RegExp(re1);
// re2 變量只是 re1 的一個副本
// 若向RegExp構(gòu)造器傳遞了第二個參數(shù),即正則表達式的標志,那么該代碼就無法工作

ES6允許使用第二個參數(shù),并且讓它覆蓋第一個參數(shù)中的標志

var re1 = /ab/i,
// ES5 中會拋出錯誤, ES6 中可用
re2 = new RegExp(re1, "g");
console.log(re1.toString()); // "/ab/i"
console.log(re2.toString()); // "/ab/g"
console.log(re1.test("ab")); // true
console.log(re2.test("ab")); // true
console.log(re1.test("AB")); // true
console.log(re2.test("AB")); // false
  • flags屬性

es6新增了與標志關(guān)聯(lián)的屬性。

在ES5中,使用source屬性來獲取正則表達式的文本。
若想獲取標志字符串,必須解析 toString() 方法的輸出。

function getFlags(re) {
    var text = re.toString();
    return text.substring(text.lastIndexOf("/") + 1, text.length);
}
// toString() 的輸出為 "/ab/g"
var re = /ab/g;
console.log(getFlags(re)); // "g"
// 將正則表達式轉(zhuǎn)換為一個字符串,并返回了最后一個 / 之后的字符,即標志。

ES6 新增了flags 屬性用于配合 source 屬性,讓標志的獲取變得更容易。
這兩個屬性均為只有 getter 的原型訪問器屬性,因此都是只讀的。
flags 屬性使得檢查正則表達式更容易,有助于調(diào)試與繼承。

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

使用 source 和 flags 允許你直接提取正則表達式的組成部分,而不必將正則表達式轉(zhuǎn)換為字符串。

  • 模板字面量

ES6的模板字面量( template literal )提供了創(chuàng)建領(lǐng)域?qū)S谜Z言( domain-specific language ,
DSL )的語法,與 ES5 及更早版本的解決方案相比,處理內(nèi)容可以更安全。
本方案通過語法糖擴展了 ECMAScript 的語法,允許語言庫提供 DSL 以便制作、查詢并
操縱來自于其它語言的內(nèi)容,并且對注入攻擊( 如 XSS 、 SQL 注入,等等 )能夠免疫
或具有抗性。
模板字面量是 ES6 針對 JS 直到 ES5 依然完全缺失的如下功能的回應:
多行字符串:針對多行字符串的形式概念;
基本的字符串格式化:將字符串部分替換為已存在的變量值的能力;
HTML 轉(zhuǎn)義:能轉(zhuǎn)換字符串以便將其安全插入到 HTML 中的能力。
模板字面量以一種新的方式解決了這些問題,而并未給 JS 已有的字符串添加額外功能。

基本語法

模板字面量的最簡單語法,是使用反引號( ` )來包裹普通字符串,而不是用雙引號或單引號。

let message = `Hello world!`;
console.log(message); // "Hello world!"
console.log(typeof message); // "string"
console.log(message.length); // 12
// 在字符串中包含反引號,只需使用反斜杠( \ )轉(zhuǎn)義即可。
let message = `\`Hello\` world!`;
console.log(message); // "`Hello` world!"
console.log(typeof message); // "string"
console.log(message.length); // 14

在模板字面量中無需對雙引號或單引號進行轉(zhuǎn)義

多行字符串

使用雙引號或單引號時,整個字符串只能放在單獨一行。

ES6 之前創(chuàng)建多行字符串的嘗試,一般都基于數(shù)組或字符串的拼接。

var message = [
    "Multiline ",
    "string"
].join("\n");
let message = "Multiline \n" +
    "string";

多行字符串的簡單解決方法,ES6 的模板字面量使多行字符串更易創(chuàng)建,因為它不需要特殊的語法。只需在想要的位置包含換行即可,而且它會顯示在結(jié)果中。

let message = `Multiline
string`;
console.log(message); // "Multiline
// string"
console.log(message.length); // 16
// 反引號之內(nèi)的所有空白符都是字符串的一部分,因此需要留意縮進。
let html = `
<div>
<h1>Title</h1>
</div>`.trim();
//如果你喜歡的話,也可以在模板字面量中使用 \n 來指示換行的插入位置:
let message = `Multiline\nstring`;
console.log(message); // "Multiline
// string"
console.log(message.length); // 16
  • 制造替換位

模板字面量與普通 JS 字符串的區(qū)別在于模板字面量的“替換位”。

替換位允許你將任何有效的 JS 表達式嵌入到模板字面量中,并將其結(jié)果輸出為字符串的一部分。

替換位由起始的 ${ 與結(jié)束的 } 來界定,之間允許放入任意的 JS 表達式。

let name = "Nicholas",
message = `Hello, ${name}.`;
console.log(message); // "Hello, Nicholas."
// 替換位 ${name} 會訪問本地變量 name ,并將其值插入到 message 字符串中。 
// message 變量會立即保留該替換位的結(jié)果。

模板字面量能訪問到作用域中任意的可訪問變量。試圖使用未定義的變量會拋出錯誤,無論是嚴格模式還是非嚴格模式。

既然替換位是 JS 表達式,那么可替換的就不僅僅是簡單的變量名。可以嵌入計算、函數(shù)調(diào)用等等。

let count = 10,
    price = 0.25,
    message = `${count} items cost $${(count * price).toFixed(2)}.`;
console.log(message); // "10 items cost $2.50."
/*
此代碼在模板字面量的一部分執(zhí)行了一次計算, count 與 price 變量相乘,再使用.toFixed() 方法將結(jié)果格式化為兩位小數(shù)。而在第二個替換位之前的美元符號被照常輸出,因為沒有左花括號緊隨其后。
*/

模板字面量本身也是 JS 表達式,意味著你可以將模板字面量嵌入到另一個模板字面量內(nèi)部。

let name = "Nicholas",
    message = `Hello, ${
`my name is ${ name }`
}.`;
console.log(message); // "Hello, my name is Nicholas."
  • 標簽化模板

一個模板標簽( template tag )能對模板字面量進行轉(zhuǎn)換并返回最終的字符串值,標簽在模板的起始處被指定,即在第一個 ` 之前。

let message = tag`Hello world`;
// tag 就是會被應用到 `Hello world` 模板字面量上的模板標簽。

定義標簽

一個標簽( tag )僅是一個函數(shù),它被調(diào)用時接收需要處理的模板字面量數(shù)據(jù)。
標簽所接收的數(shù)據(jù)被劃分為獨立片段,并且必須將它們組合起來以創(chuàng)建結(jié)果。
第一個參數(shù)是個數(shù)組,包含被 JS 解釋過的字面量字符串,隨后的參數(shù)是每個替換位的解釋值。
標簽函數(shù)的參數(shù)一般定義為剩余參數(shù)形式,以便更容易處理數(shù)據(jù)。

function tag(literals, ...substitutions) {
    // 返回一個字符串
}
let count = 10,
price = 0.25,
message = passthru`${count} items cost $${(count * price).toFixed(2)}.`;
/*
passthru() 函數(shù),該函數(shù)將會接收到三個參數(shù)。
首先是一個literals 數(shù)組,包含如下元素:
在首個替換位之前的空字符串( "" );
首個替換位與第二個替換位之間的字符串( " items cost $" );
第二個替換位之后的字符串( "." )。
substitutions.length === literals.length - 1 的值總是 true
*/
function passthru(literals, ...substitutions) {
    let result = "";
    // 僅使用 substitution 的元素數(shù)量來進行循環(huán)
    for (let i = 0; i < substitutions.length; i++) {
        result += literals[i];
        result += substitutions[i];
    }
    // 添加最后一個字面量
    result += literals[literals.length - 1];
    return result;
}
let count = 10,
    price = 0.25,
    message = passthru `${count} items cost $${(count * price).toFixed(2)}.`;
console.log(message); // "10 items cost $2.50."

/*
執(zhí)行與模板字面量的默認行為相同的轉(zhuǎn)換操作。
在循環(huán)中使用 substituions.length 而不是 literals.length 來避免 substituions 數(shù)組的越界。
它能工作是由于 ES6 對 literals 和 substituions 的良好定義。

*/

使用模板字面量中的原始值

模板標簽能訪問字符串的原始信息,主要指的是可以訪問字符在轉(zhuǎn)義之前的形式。
可使用內(nèi)置的 String.raw() 標簽。

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

字符串的原始信息同樣會被傳遞給模板標簽。
標簽函數(shù)的第一個參數(shù)為包含額外屬性 raw 的數(shù)組,而 raw 屬性則是含有與每個字面量值等價的原始值的數(shù)組。

例如, literals[0] 的值總是等價于包含字符串原始信息的 literals.raw[0] 的值

function raw(literals, ...substitutions) {
    let result = "";
    // 僅使用 substitution 的元素數(shù)量來進行循環(huán)
    for (let i = 0; i < substitutions.length; i++) {
        result += literals.raw[i]; // 改為使用原始值
        result += substitutions[i];
    }
    // 添加最后一個字面量
    result += literals.raw[literals.length - 1];
    return result;
}
let message = raw `Multiline\nstring`;
console.log(message); // "Multiline\\nstring"
console.log(message.length); // 17
/*
這里使用 literals.raw 而非 literals 來輸出結(jié)果字符串。
這意味著任何轉(zhuǎn)義字符都會以原始的形式返回。
當你想在輸出的字符串中包含轉(zhuǎn)義字符時,原始字符串會很有幫助。
*/
  • 總結(jié)

完整的 Unicode 支持允許 JS 以合理的方式處理 UTF-16 字符。通過 codePointAt() 與
String.fromCodePoint() 在代碼點和字符之間轉(zhuǎn)換的能力,是字符串操作的一大進步。正則
表達式新增的 u 標志使得直接操作代碼點而不是 16 位字符變?yōu)榭赡埽?normalize() 方
法則允許進行更恰當?shù)淖址容^。
ES6 也添加了操作字符串的新方法,允許你更容易識別子字符串,而不用管它在父字符串中
的位置。正則表達式同樣引入了許多功能。
模板字面量是 ES6 的一項重要補充,允許你創(chuàng)建領(lǐng)域?qū)S谜Z言( DSL )讓字符串的創(chuàng)建更容
易。能將變量直接嵌入到模板字面量中,意味著開發(fā)者在組合長字符串與變量時,有了一種
比字符串拼接更為安全的工具。
內(nèi)置的多行字符串支持,是普通 JS 字符串絕對無法做到的,這使得模板字面量成為凌駕于前
者之上的有用升級。盡管在模板字面量中允許直接使用換行,你依然可以使用 \n 或其它字
符轉(zhuǎn)義序列。
模板標簽是創(chuàng)建 DSL 最重要的部分。標簽是接收模板字面量片段作為參數(shù)的函數(shù),你可以使
用它們來返回合適的字符串。這些數(shù)據(jù)包括了字面量、等價的原始值以及替換位的值,標簽
使用這些信息片段來決定輸出。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容