JavaScript容許採用 \uxxxx
形式表示一個字符,其中 xxxx
表示字符的 Unicode
碼點。正則表達式
"\u0061" // 表示小寫字母"a"
可是這種表示法只限於碼點在 \u0000-\uFFFF
之間的字符,有些字符的 Unicode
編碼超出了這個範圍,那麼就必須使用2個雙字節的形式表示。數組
"\uD842\uDFB7" // "𠮷" 注意不是吉祥的"吉" "\u5409" // "吉" 這個纔是吉祥的"吉"
在 ES5
中若是在 \u
後面超過 oxFFFF
的數值,如 "\u0061我"
輸出結果爲 a我
即 "\u0061我"
在 JS
引擎看來就是 "\u0061+'我'"
後面的經過字符串拼接拼接上。函數
ES6
對這一點作出了改進,只要將碼點放入大括號,就能正確解讀該字符。測試
例如 \u20BB7
表示的是 "𠮷"
,在 ES5
下 "\u20BB7"
被 JS
引擎解析成 "7"
,這是由於 \u20BB
是一個不可打印的字符,因此只會顯示一個空格,後面拼接上一個 7
.this
在 ES6
中 "\u{20BB7}"
的解析結果爲 "𠮷"
.編碼
JavaScript
內部,字符以 UTF-16
的格式存儲,每一個字符固定爲2個字節(範圍 \u0000-\uFFFF
),可是有些字符的碼點是大於 0xFFFF
的,JavaScript
會認爲它是兩個字符。設計
var s1 = "你好"; var s2 = "𠮷"; s1.length // 2 s2.length // 2
這是由於 𠮷
的碼點大於 0xFFFF
,JS
引擎認爲它是兩個字符,即佔四個字節。code
s2.charCodeAt(0) // 55362 s2.charCodeAt(0) // 57271 s2.codePointAt(0) // 134071=0X20BB7 s2.codePointAt(1) // 57271
能夠看出 charCodeAt
方法一次只能返回兩個字節的值,而 codePointAt(0)
能夠返回四個字節的值,codePointAt(1)
與 charCodeAt(1)
返回的值相同。orm
var s = "𠮷a"; s.codePointAt(0) // "13401" 對應"𠮷" s.codePointAt(2) // "61" 對應"a"
能夠看到在傳入 2
時才能獲得第二個字符 a
, 能夠經過 for...of
循環來解決這個問題,由於它會正確識別32位的 UTF-16
字符。對象
var s = "𠮷a"; for (let ch of s) { console.log(ch.codePointAt(0).toString(16)); } // 20bb7 // 61
codePointAt()
方法還能夠用來測試一個字符是2個字節仍是4個字節。
function is32Bit(c) { return c.codePointAt(0) > 0xFFFF; } is32Bit("𠮷"); // true is32Bit("a"); // false
ES5
提供了 String.fromCharCode
方法,用於從碼點返回對應字符,可是這個方法不能識別32位的 UTF-16
字符(Unicode 編號大於 0xFFFF
)。
String.fromCharCode(0x20061); // 'a'
String.fromCharCode
方法不能識別大於 0xFFFF
的碼點,因此 0x20BB7
就發生了溢出,最高位2被捨棄,最後返回碼點 U+0061
對應的字符 a
。
ES6
提供了 String.fromCodePoint
方法,能夠識別大於 0xFFFF
的字符,做用上與 codePointAt
方法正好相反。
String.fromCodePoint(0x78, 0x1f680, 0x79) ==== 'x\uD83D\UDE80y';
String.fromCharCode
方法若是有多個參數,那麼它們就會拼接成一個字符串。
注意: fromCodePoint
方法定義在 String
對象上,而 codePointAt
方法定義在字符串的實例對象上。
ES6
爲字符串添加了遍歷器接口,使得字符串能夠由 for...of
循環遍歷。
for (let codePoint of 'foo') { console.log(codePoint); } // 'f' // 'o' // 'o'
前面已經提到過,利用 for...of
能夠識別大於 0xFFFF
的碼點,傳統的 for
循環沒法識別大於 0xFFFF
的碼點。
var text = String.fromCodePoint(0x20BB7); for (let i = 0, length = text.length; i < length; i++) { console.log(text[i]); } // '' // '' 輸出兩個不可打印的字符 for (let i of text) { console.log(i); } // '𠮷'
在上面的代碼中 0x20BB7
只有一個字符,可是 for
循環認爲它包含 2
個字符,而 for...of
循環會正確識別出這個字符。
ES5
中爲字符串對象提供了 charAt
方法,返回字符串給定位置的字符,該方法不能識別碼點大於 0xFFFF
的字符。
'abc'.charAt(0) // 'a' '𠮷'.charAt(0) // '\uD842',一個不可打印的字符
charAt
方法只能返回 UTFF-16
編碼中的第一個字節,目前有一個提案提出字符串實例的 at
方法,能夠識別 Unicode
編號大於 0xFFFF
的字符,返回正確的字符。
'𠮷'.charAt(0) // '𠮷',能夠正確返回
許多歐洲語言有重音符號和語調符號,爲了表示它們, Unicode
提供了兩種方法:
可是合成符號在 JS
引擎看來其實是兩個字符,合成字符並不等於帶重音符號的字符。
normalize
方法解決了這個問題,將字符的不一樣表示方法統一爲一樣的形式,這稱爲 Unicode
正規化。
不過 nomalize
方法目前不能識別三個或三個以上字符的合成,這種狀況下仍是隻能利用正則表達式,經過 Unicode
編號區間判斷。
ES5:
indexOf
肯定一個字符串是否包含在另外一個字符串中。ES6:
includes
返回布爾值,表示是否找到了傳入的參數字符串startWith
返回布爾值,表示參數字符串是否在源字符串的頭部endWith
返回布爾值,表示參數字符串是否在源字符串的尾部var s = "hello JS"; s.includes("ell"); // true s.startWith("h"); // true s.endWith("S"); // true
這三個方法都支持第二個參數,表示開始搜索的位置。
var s = "hello JS"; s.includes("JS", 6); // true, 從第個7個(從0開始)字符位置開始 s.startWith("JS", 6); // true, 從第7個(從0開始)字符位置開始 s.endWith("hello", 5); // false, 前5個字符(下標爲0,1,2,3,4)
功能:返回一個新字符串,將原字符串重複傳入的參數次。
'x'.repeat(2); // "xx"
若是傳入的參數是小數,會被取整。
'x'.repeat(2.9) // 'xx'
'x'.repeat(-0.9) // ''
'x'.repeat(-2) // RangeError
若是傳入的參數不是數字,會先將其準換爲數字。
'x'.repeat('x'); // '' 'x'.repeat('2'); // 'xx'
ES2017
引入了字符串補全長度的功能,若是某個字符串長度不夠指定長度,會在頭部或尾部補全。
padStart()
用於頭部補全padEnd()
用於尾部補全上面兩個方法都接收兩個參數,第一個參數用來指定字符串的最小長度,第二個參數是用來補全的字符串。
'x'.padStart(5, 'ab'); // 'ababx' 'x'.padEnd(5, 'ab'); // 'xabab'
若是原字符串的長度等於或大於指定的最小長度,則返回原字符串。
'xxx'.padStart(2, 'ab'); // 'xxx' 'xxx'.padEnd(2, 'ab'); // 'xxx'
若是補全的字符串與原字符串的長度之和大於指定的最小長度,則會截去超出位數的補全字符串。
'xxx'.padStart(5, '01234'); // '01xxx'
若是省略第二個參數,則會用空格來補全。
'x'.padStart(4); // ' x'
padStart()
的兩種經常使用用途:
'1'.padStart(10,'0'); // '0000000001'
'12'.padStart(10, 'YYYY-MM-DD'); // 'YY-MM-12'
模板字符串是加強版的字符串,用反引號 `` ` 來標識,主要有如下三種用法。
`hello ES6`
定義多行字符串
全部的空格和縮進都會保留在輸出中。
` hello ES6 ` // 全部的空格和換行都會被保留,輸出結果: " hello ES6 "
trim()
方法能夠消除模板字符串反引號 `` ` 和模板字符串內容之間的空格和換行。
注意:模板字符串中的字符串之間的空格和換行是不受影響的
` hello ES6 `.trim(); //輸出結果: " hello ES6 "
用單引號或雙引號定義的字符串是不能有換行符的,不然會報錯。
" hello ES6 " // 輸出結果: //Uncaught SyntaxError: Invalid or unexpected token
在字符串中嵌入變量
在模板字符串中嵌入變量須要將變量名放在 ${}
中, {}
中實際上能夠是任意的 JavaScript
表達式,能夠進行運算,引用對象屬性等,若是大括號中的值不是字符串,則會按照必定的規則將其轉換爲字符串。
var name = 'zhangsan', age = 18; `hello, my name is ${name}, I am ${age} years old` // 輸出結果: hello, my name is zhangsan, I aam 18 years old
模板字符串能夠緊跟在一個函數名後面,這個函數將會被調用來處理這個模板字符串,這被稱爲「標籤模板」功能。
alert `123` // 等同於 alert (123)
標籤模板實際上不是模板,而是函數調用的一種特殊形式,「標籤」指的就是函數,緊跟在它後面的模板字符串就是它的參數。
若是模板字符串中有變量,就再也不是簡單的調用了,而是將模板字符串先處理成多個參數,再調用函數。
var a = 5; var b = 10; tag `hello ${ a + b } world ${ a * b }`; // 等同於 tag(["hello ", " world ", ""], 15, 50);
tag
函數的第一個參數是一個數組,該數組的成員是模板字符串中那些沒有變量替換的部分。
tag
函數全部參數的實際值以下:
["hello ", " world ", ""]
15
50
標籤模板的兩個重要應用
過濾HTML字符串,防止用戶輸入惡意內容
function saferHTML(templateData) { var s = templateData[0]; // templateData=["<p>"," has sent you a message.</p>"] for (var i = 1; i < arguments.length; i++) { var arg = String(arguments[i]); // argument[1]="<script>alert('abc')</script>" s += arg.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">"); s += templateData[i]; } return s; } var sender = "<script>alert('abc')</script>"; // 惡意代碼 var message = saferHTML`<p>${sender} has sent you a message.</p>`; 輸出結果: // <p><script>alert('abc')</script> has sent you a message.</p>
咱們通常要保證用戶輸入的內容中不能含有可執行的 JS
代碼,這是爲了防止那些黑客將這些代碼植入到咱們的程序中對咱們的程序進行攻擊。
所以,咱們要將 script
標籤等一切可引入 JS
代碼的方式都過濾掉,上面的程序中只考慮 script
標籤可引入 JS
代碼這一種方式。
多語言轉換
這裏的語言不只指中文,英文這種語言之間的轉換,還指在 JS
中還能夠運行其它的計算機語言。
固然模板字符串自己並不具備這樣的功能,這種功能的完成是依靠一些標籤模板來完成的。
i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!` // 輸出結果: // "歡迎訪問xxx, 您是第xxx位訪問者!"
i18n
函數能夠將英文轉換爲中文。
jsx` <div> <input ref = 'input' onChange = '${this.handleChange}' defaultValue = '${this.state.value}' /> ${this.state.value} </div> `
上面的代碼是經過 jsx
函數將一個 DOM
字符串轉換爲 React
對象。
模板處理函數的第一個參數(模板字符串數組)還有一個raw屬性,raw屬性中保存的是轉義後的原字符串。
tag`First line\nSecond line` function tag (string) { console.log(string.raw[0]); } // 輸出結果: // "First line \\nSecond line" 保存的是轉義後的字符串 // 而string = "First line\nSecond line"
string
和 string.raw
惟一的區別就在於 string.raw
裏面保存的是轉義後的字符串。這是爲了方便取得轉義以前的原始模板而設計的。
ES6
爲 String
對象提供了一個 raw
方法。
String.raw()
方法每每用來充當模板字符串的處理函數,返回一個連反斜線都會轉義的字符串。
String.raw`hello\n{2+3}`; // "hello\\n5"
String.raw()
方法也能夠看成正常的函數使用,可是第一個參數必須是一個就有raw
屬性的對象,而且 raw
屬性的值必須是一個數組。
一句話總結就是模板字符串會將字符串進行轉義,好比
function latex { ... } let document = latex` \newcommand{\unicode}{\textbf{Unicode!}}` // 報錯
這是由於 \u
在 LaTex
中具備特殊的含義,可是 JS
將它們進行了轉義。
爲了解決這個問題,有個提案提出放鬆對標籤模板裏字符串轉義的限制,如遇到不合法的字符串轉義,就返回 undefined
,而不是報錯。