礎用法
let message=`Hello World`; console.log(message);
如果你碰巧要在字符串中使用反撇號,你可以使用反斜杠轉義:
let message=`Hello \` World`; console.log(message);
值得一提的是,在模板字符串中,空格、縮進、換行都會被保留:
let message=` <ul> <li>1</li> <li>2</li> </ul> `; console.log(message);
注意,打印的結果中第一行是一個換行,你可以使用 trim 函數消除換行:
let message=` <ul> <li>1</li> <li>2</li> </ul> `.trim(); console.log(message);
嵌入變量
模板字符串支持嵌入變量,只需要將變量名寫在 ${} 之中,其實不止變量,任意的 JavaScript 表達式都是可以的:
let x=1, y=2; let message=`<ul><li>${x}</li><li>${x + y}</li></ul>`; console.log(message); // <ul><li>1</li><li>3</li></ul>
值得一提的是,模板字符串支持嵌套:
let arr=[{value: 1}, {value: 2}]; let message=` <ul> ${arr.map((item)=> { return ` <li>${item.value}</li> ` })} </ul> `; console.log(message);
打印結果如下:
注意,在 li 標簽中間多了一個逗號,這是因為當大括號中的值不是字符串時,會將其轉為字符串,比如一個數組 [1, 2, 3] 就會被轉為 1,2,3,逗號就是這樣產生的。
如果你要消除這個逗號,你可以先 join 一下:
let arr=[{value: 1}, {value: 2}]; let message=` <ul> ${arr.map((item)=> { return ` <li>${item.value}</li> ` }).join('')} </ul> `; console.log(message);
打印結果如下:
標簽模板
模板標簽是一個非常重要的能力,模板字符串可以緊跟在一個函數名后面,該函數將被調用來處理這個模板字符串,舉個例子:
let x='Hi', y='Kevin'; var res=message`${x}, I am ${y}`; console.log(res);
我們可以自定義 message 函數來處理返回的字符串:
// literals 文字 // 注意在這個例子中 literals 的第一個元素和最后一個元素都是空字符串 function message(literals, value1, value2) { console.log(literals); // [ "", ", I am ", "" ] console.log(value1); // Hi console.log(value2); // Kevin }
我們利用這些參數將其拼合回去:
function message(literals, ...values) { let result=''; for (let i=0; i < values.length; i++) { result +=literals[i]; result +=values[i]; } result +=literals[literals.length - 1]; return result; }
你也可以這樣寫:
function message(literals, ...values) { let result=literals.reduce((prev, next, i)=> { let value=values[i - 1]; return prev + value + next; }); return result; }
學著拼合回去是一件非常重要的事情,因為我們經過各種處理,最終都還是要拼回去的……
oneLine
講完了基礎,我們可以來看一些實際的需求:
let message=` Hi, Daisy! I am Kevin. `;
出于可讀性或者其他原因,我希望書寫的時候是換行的,但是最終輸出的字符是在一行,這就需要借助模板標簽來實現了,我們嘗試寫一個這樣的函數:
// oneLine 第一版 function oneLine(template, ...expressions) { let result=template.reduce((prev, next, i)=> { let expression=expressions[i - 1]; return prev + expression + next; }); result=result.replace(/(\s+)/g, " "); result=result.trim(); return result; }
實現原理很簡單,拼合回去然后將多個空白符如換行符、空格等替換成一個空格。
使用如下:
let message=oneLine ` Hi, Daisy! I am Kevin. `; console.log(message); // Hi, Daisy! I am Kevin.
不過你再用下去就會發現一個問題,如果字符間就包括多個空格呢?舉個例子:
let message=oneLine` Preserve eg sentences. Double spaces within input lines. `;
如果使用這種匹配方式,sentences. 與 Double 之間的兩個空格也會被替換成一個空格。
我們可以再優化一下,我們想要的效果是將每行前面的多個空格替換成一個空格,其實應該匹配的是換行符以及換行符后面的多個空格,然后將其替換成一個空格,我們可以將正則改成:
result=result.replace(/(\n\s*)/g, " ");
就可以正確的匹配代碼。最終的代碼如下:
// oneLine 第二版 function oneLine(template, ...expressions) { let result=template.reduce((prev, next, i)=> { let expression=expressions[i - 1]; return prev + expression + next; }); result=result.replace(/(\n\s*)/g, " "); result=result.trim(); return result; }
stripIndents
假設有這樣一段 HTML:
let html=` <span>1<span> <span>2<span> <span>3<span> `;
為了保持可讀性,我希望最終輸入的樣式為:
<span>1<span> <span>2<span> <span>3<span>
其實就是匹配每行前面的空格,然后將其替換為空字符串。
// stripIndents 第一版 function stripIndents(template, ...expressions) { let result=template.reduce((prev, next, i)=> { let expression=expressions[i - 1]; return prev + expression + next; }); result=result.replace(/\n[^\S\n]*/g, '\n'); result=result.trim(); return result; }
最難的或許就是這個正則表達式了:
result=result.replace(/\n[^\S\n]*/g, '\n');
\S 表示匹配一個非空白字符
[^\S\n] 表示匹配非空白字符和換行符之外的字符,其實也就是空白字符去除換行符
\n[^\S\n]* 表示匹配換行符以及換行符后的多個不包含換行符的空白字符
replace(/\n[^\S\n]*/g, '\n') 表示將一個換行符以及換行符后的多個不包含換行符的空白字符替換成一個換行符,其實也就是將換行符后面的空白字符消掉的意思
其實吧,不用寫的這么麻煩,我們還可以這樣寫:
result=result.replace(/^[^\S\n]+/gm, '');
看似簡單了一點,之所以能這樣寫,是因為匹配模式的緣故,你會發現,這次除了匹配全局之外,這次我們還匹配了多行,m 標志用于指定多行輸入字符串時應該被視為多個行,而且如果使用 m 標志,^ 和 $ 匹配的開始或結束是輸入字符串中的每一行,而不是整個字符串的開始或結束。
[^\S\n] 表示匹配空白字符去除換行符
^[^\S\n]+ 表示匹配以去除換行符的空白字符為開頭的一個或者多個字符
result.replace(/^[^\S\n]+/gm, '') 表示將每行開頭一個或多個去除換行符的空白字符替換成空字符串,也同樣達到了目的。
最終的代碼如下:
// stripIndents 第二版 function stripIndents(template, ...expressions) { let result=template.reduce((prev, next, i)=> { let expression=expressions[i - 1]; return prev + expression + next; }); result=result.replace(/^[^\S\n]+/gm, ''); result=result.trim(); return result; }
stripIndent
注意,這次的 stripIndent 相比上面一節的標題少了一個字母 s,而我們想要實現的功能是:
let html=` <ul> <li>1</li> <li>2</li> <li>3</li> <ul> `;
其實也就是去除第一行的換行以及每一行的部分縮進。
這個實現就稍微麻煩了一點,因為我們要計算出每一行到底要去除多少個空白字符。
實現的思路如下:
實現的代碼如下:
let html=` <ul> <li>1</li> <li>2</li> <li>3</li> <ul> `; function stripIndent(template, ...expressions) { let result=template.reduce((prev, next, i)=> { let expression=expressions[i - 1]; return prev + expression + next; }); const match=result.match(/^[^\S\n]*(?=\S)/gm); console.log(match); // Array [ " ", " ", " ", " ", " " ] const indent=match && Math.min(...match.map(el=> el.length)); console.log(indent); // 4 if (indent) { const regexp=new RegExp(`^.{${indent}}`, 'gm'); console.log(regexp); // /^.{4}/gm result=result.replace(regexp, ''); } result=result.trim(); return result; }
值得一提的是,我們一般會以為正則中 . 表示匹配任意字符,其實是匹配除換行符之外的任何單個字符。
最終精簡的代碼如下:
function stripIndent(template, ...expressions) { let result=template.reduce((prev, next, i)=> { let expression=expressions[i - 1]; return prev + expression + next; }); const match=result.match(/^[^\S\n]*(?=\S)/gm); const indent=match && Math.min(...match.map(el=> el.length)); if (indent) { const regexp=new RegExp(`^.{${indent}}`, 'gm'); result=result.replace(regexp, ''); } result=result.trim(); return result; }
includeArrays
前面我們講到為了避免 ${} 表達式中返回一個數組,自動轉換會導致多個逗號的問題,需要每次都將數組最后再 join('') 一下,再看一遍例子:
let arr=[{value: 1}, {value: 2}]; let message=` <ul> ${arr.map((item)=> { return ` <li>${item.value}</li> ` }).join('')} </ul> `; console.log(message);
利用標簽模板,我們可以輕松的解決這個問題:
跨網站指令碼(英語:Cross-site scripting,通常簡稱為:XSS)是一種網站應用程式的安全漏洞攻擊,是代碼注入的一種。它允許惡意使用者將程式碼注入到網頁上,其他使用者在觀看網頁時就會受到影響。這類攻擊通常包含了 HTML 以及使用者端腳本語言
XSS 分為三種:反射型,存儲型和 DOM-based
如何攻擊
<!-- http://www.domain.com?name=<script>alert(1)</script> -->
<div>{{name}}</div>
上述 URL 輸入可能會將 HTML 改為 <div><script>alert(1)</script></div> ,這樣頁面中就憑空多了一段可執行腳本。這種攻擊類型是反射型攻擊,也可以說是 DOM-based 攻擊
如何防御
最普遍的做法是轉義輸入輸出的內容,對于引號,尖括號,斜杠進行轉義
function escape(str) {
str=str.replace(/&/g, "&");
str=str.replace(/</g, "<");
str=str.replace(/>/g, ">");
str=str.replace(/"/g, "&quto;");
str=str.replace(/'/g, "&##39;");
str=str.replace(/`/g, "&##96;");
str=str.replace(/\//g, "&##x2F;");
return str
}
通過轉義可以將攻擊代碼 <script>alert(1)</script> 變成
// -> <script>alert(1)<&##x2F;script>
escape('<script>alert(1)</script>')
對于顯示富文本來說,不能通過上面的辦法來轉義所有字符,因為這樣會把需要的格式也過濾掉。這種情況通常采用白名單過濾的辦法,當然也可以通過黑名單過濾,但是考慮到需要過濾的標簽和標簽屬性實在太多,更加推薦使用白名單的方式
var xss=require("xss");
var html=xss('<h1 id="title">XSS Demo</h1><script>alert("xss");</script>');
// -> <h1>XSS Demo</h1><script>alert("xss");</script>
console.log(html);
以上示例使用了 js-xss來實現。可以看到在輸出中保留了 h1 標簽且過濾了 script 標簽
跨站請求偽造(英語:Cross-site request forgery),也被稱為 one-click attack或者 session riding,通常縮寫為 CSRF 或者 XSRF, 是一種挾制用戶在當前已登錄的Web應用程序上執行非本意的操作的攻擊方法
CSRF 就是利用用戶的登錄態發起惡意請求
如何攻擊
假設網站中有一個通過 Get 請求提交用戶評論的接口,那么攻擊者就可以在釣魚網站中加入一個圖片,圖片的地址就是評論接口
<img src="http://www.domain.com/xxx?comment='attack'"/>
如何防御
加鹽
對于密碼存儲來說,必然是不能明文存儲在數據庫中的,否則一旦數據庫泄露,會對用戶造成很大的損失。并且不建議只對密碼單純通過加密算法加密,因為存在彩虹表的關系
// 加鹽也就是給原密碼添加字符串,增加原密碼長度
sha256(sha1(md5(salt + password + salt)))
但是加鹽并不能阻止別人盜取賬號,只能確保即使數據庫泄露,也不會暴露用戶的真實密碼。一旦攻擊者得到了用戶的賬號,可以通過暴力破解的方式破解密碼。對于這種情況,通常使用驗證碼增加延時或者限制嘗試次數的方式。并且一旦用戶輸入了錯誤的密碼,也不能直接提示用戶輸錯密碼,而應該提示賬號或密碼錯誤
前端加密
雖然前端加密對于安全防護來說意義不大,但是在遇到中間人攻擊的情況下,可以避免明文密碼被第三方獲取
JavaScript 字符串用于存儲和處理文本。
JavaScript 字符串
字符串可以存儲一系列字符,如 "John Doe"。
字符串可以是插入到引號中的任何字符。你可以使用單引號或雙引號:
實例
var carname="Volvo XC60";
var carname='Volvo XC60';
你可以使用索引位置來訪問字符串中的每個字符:
實例
var character=carname[7];
字符串的索引從 0 開始,這意味著第一個字符索引值為 [0],第二個為 [1], 以此類推。
你可以在字符串中使用引號,字符串中的引號不要與字符串的引號相同:
實例
var answer="It's alright";
var answer="He is called 'Johnny'";
var answer='He is called "Johnny"';
你也可以在字符串添加轉義字符來使用引號:
實例
var x='It\'s alright';
var y="He is called \"Johnny\"";
字符串長度
可以使用內置屬性 length 來計算字符串的長度:
實例
var txt="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var sln=txt.length;
特殊字符
在 JavaScript 中,字符串寫在單引號或雙引號來中。
因為這樣,以下實例 JavaScript 無法解析:x
"We are the so-called "Vikings" from the north."
字符串 "We are the so-called " 被截斷。
如何解決以上的問題呢?可以使用反斜杠 (\) 來轉義 "Vikings" 字符串中的雙引號,如下:
"We are the so-called \"Vikings\" from the north."
反斜杠是一個轉義字符。 轉義字符將特殊字符轉換為字符串字符:
轉義字符 (\) 可以用于轉義撇號,換行,引號,等其他特殊字符。
下表中列舉了在字符串中可以使用轉義字符轉義的特殊字符:
代碼 | 輸出 |
---|---|
\' | 單引號 |
\" | 雙引號 |
\ | 反斜杠 |
\n | 換行 |
\r | 回車 |
\t | tab(制表符) |
\b | 退格符 |
\f | 換頁符 |
字符串可以是對象
通常, JavaScript 字符串是原始值,可以使用字符創建: var firstName="John"
但我們也可以使用 new 關鍵字將字符串定義為一個對象: var firstName=new String("John")
實例
var x="John";
var y=new String("John");
typeof x // 返回 String
typeof y // 返回 Object
不要創建 String 對象。它會拖慢執行速度,并可能產生其他副作用: |
實例
var x="John";
var y=new String("John");
(x===y) // 結果為 false,因為是字符串,y 是對象
===為絕對相等,即數據類型與值都必須相等。
字符串屬性和方法
原始值字符串,如 "John", 沒有屬性和方法(因為他們不是對象)。
原始值可以使用 JavaScript 的屬性和方法,因為 JavaScript 在執行方法和屬性時可以把原始值當作對象。
字符串屬性
屬性 | 描述 |
---|---|
constructor | 返回創建字符串屬性的函數 |
length | 返回字符串的長度 |
prototype | 允許您向對象添加屬性和方法 |
字符串方法
Method | 描述 |
---|---|
charAt() | 返回指定索引位置的字符 |
charCodeAt() | 返回指定索引位置字符的 Unicode 值 |
concat() | 連接兩個或多個字符串,返回連接后的字符串 |
fromCharCode() | 將 Unicode 轉換為字符串 |
indexOf() | 返回字符串中檢索指定字符第一次出現的位置 |
lastIndexOf() | 返回字符串中檢索指定字符最后一次出現的位置 |
localeCompare() | 用本地特定的順序來比較兩個字符串 |
match() | 找到一個或多個正則表達式的匹配 |
replace() | 替換與正則表達式匹配的子串 |
search() | 檢索與正則表達式相匹配的值 |
slice() | 提取字符串的片斷,并在新的字符串中返回被提取的部分 |
split() | 把字符串分割為子字符串數組 |
substr() | 從起始索引號提取字符串中指定數目的字符 |
substring() | 提取字符串中兩個指定的索引號之間的字符 |
toLocaleLowerCase() | 根據主機的語言環境把字符串轉換為小寫,只有幾種語言(如土耳其語)具有地方特有的大小寫映射 |
toLocaleUpperCase() | 根據主機的語言環境把字符串轉換為大寫,只有幾種語言(如土耳其語)具有地方特有的大小寫映射 |
toLowerCase() | 把字符串轉換為小寫 |
toString() | 返回字符串對象值 |
toUpperCase() | 把字符串轉換為大寫 |
trim() | 移除字符串首尾空白 |
valueOf() | 返回某個字符串對象的原始值 |
如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。