標籤模板
- “標籤”指的就是函數,緊跟在後面的模板字符串就是它的參數。
- 如果模板字符裏面有變量,會將模板字符串先處理成多個參數,再調用函數。
- 函數的第一個參數是一個數組,該數組的成員是模板字符串中那些沒有變量替換的部分。
假設有以下函數:
let a = 1;
let b = 2;
function tag(x,y){
return x,y;
}
tag`this is ${a} and ${a+b}`;
注意,由於使用模板字符串形式,第一個參數會是一個只接收非變量的數組(存在一個raw屬性,用來保存轉以後的原字符串),所以這裏會輸出,可以看到輸出參數並不全,因爲非變量數組佔用了一個變量位置:
["this is" , " and " , "" ] 1
//x即爲存放非變量的數組,故y爲第二個參數1
相當於這麼調用的函數:
tag(["this is","and",""],1,3);
所以如果想取到全部的參數可以:
//1,對應相同數量的參數
function tag(x,y,z){
return x,y,z // y = 1 , z = 3
}
//2,使用reset參數
function tag(x , ...y){
return x,y; //x = ['this is' , 'and' , ""] y = [1,3]
}
//3,arugments
function tag(x){
return arguments; //0:['this is' , 'and' , ''] 1:1 2:3
}
按照調用的語句拼接:
function tag(x,...y){
let output = '';
y.forEach((item,index)=>{
output += x[index] + item;
})
output += x[y.length];
return output; // this is 1 and 3
}
常見用法:防止用戶惡意插入代碼進行XSS攻擊,場景:(借鑑自https://blog.csdn.net/qq_43287488/article/details/82873336)
1、攻擊者通過評論表單提交將<script>alert(‘aaa’)</script>
提交到網站
2、網站後端對提交的評論數據不做任何操作,直接存儲到數據庫中
3、其他用戶訪問正常訪問網站,並且需要請求網站的評論數據
4、網站後端會從數據庫中取出數據,直接返回給用戶
5、用戶得到頁面後,直接運行攻擊者提交的代碼<script>alert(‘aaa’)</script>
,所有用戶都會在網頁中彈出aaa的彈窗
應用:
function safeHTML(templateData,...reset){
let s = templateData[0];
reset.foreach((item,index)=>{
let arg = String(item);
s += arg.replace(/&/g,"&") //將&轉換爲字符串&amg
.replace(/</g,"<") //將<轉換爲字符串<
.replace(/>/g,">"); //將>轉換爲>
s += templateData[index];
})
return s; //返回轉義後的字符串
}
let sender = '<script>alert("abc") < /script>'; // 用戶輸入的惡意代碼
let message = SaferHTML`<p>${sender} has sent you a message.</p>`;
// message -> <p><script>alert("abc") < /script><p>
標籤函數與模板編譯結合,參照官網例子:
// 下面的hashTemplate函數
// 是一個自定義的模板處理函數
let libraryHtml = hashTemplate`
<ul>
#for book in ${myBooks}
<li><i>#{book.title}</i> by #{book.author}</li>
#end
</ul>
`;
按照自己想法處理了一下,主要原理參照模板編譯,正則替換爲JavaScript表達式進行解析:
let myBooks = [{
title: "ES6",
author: "Javascript"
}];
let libraryHtml = hashTemplate `
<ul>
#for book of ${myBooks}
<li><i>#{book.title}</i> by #{book.author}</li>
#end
</ul>
`;
function hashTemplate(template, ...reset) {
let output = '';
//正則表達式轉換
let delbrackets = /<(.+?)>/g , //()中的內容
delbraces = /#{(.+?)}/g , //{}中的內容
changefor = /#for/g, //#for的更改
changeend = /#end/g; //#end的替換
template.forEach((element, index) => {
//用三元判斷拼接的順序
index > 0 ? output += element : output += element + 'myBooks){';
});
//使用replace進行替換,by字符串實屬無奈,學藝不精
output = output
.replace(delbrackets, 'echo(`<$1>`) \n')
.replace(delbraces, '\n echo($1)\n')
.replace(changefor, 'for(')
.replace(changeend, '}')
.replace(/(by)/g, ' echo(`$1`) ');
let script =
`
(function parse(myBooks){
let html = '';
function echo(str){
html+=str;
}
${output}
return html;
})
`
let parse = eval(script);
document.querySelector('div').innerHTML = parse(...reset);
}
該方法類似於模板編譯實例的解法,更改完輸出的字符串爲:
(function parse(myBooks) {
let html = '';
function echo(str) {
html += str;
}
echo(`<ul>`)
for (book of myBooks) {
echo(`<li>`)
echo(`<i>`)
echo(book.title)
echo(`</i>`)
echo(`by`)
echo(book.author)
echo(`</li>`)
}
echo(`</ul>`)
return html;
})
String.raw()
用來充當模板字符串的處理函數,返回一個斜槓都被轉義(即斜槓前面再加一個斜槓)的字符串。
String.raw`Hi\n${2+3}!`;
// 返回 "Hi\\n5!"
String.raw`Hi\u000A!`;
// 返回 "Hi\\u000A!"
正則的擴展(暫時跳過)
數值的擴展
新增方法:
- Number.isFinite() 用來檢查一個數值是否爲有限的(finite)
- Number.isNaN()用來檢查一個值是否爲NaN。
-
而這兩個新方法只對數值有效,Number.isFinite()對於非數值一律返回false, Number.isNaN()只有對於NaN才返回true,非NaN一律返回false。
-
ES6 將全局方法parseInt()和parseFloat(),移植到Number對象上面,Number.parseInft(),Number.parseFloat()。
-
Number.isInteger()用來判斷一個數值是否爲整數,如果數值的精度超過這個限度,第54位及後面的位就會被丟棄,這種情況下,Number.isInteger可能會誤判。
-
如果對數據精度的要求較高,不建議使用Number.isInteger()判斷一個數值是否爲整數。
-
Number.EPSILON,JavaScript 能夠表示的最小精度,實質是一個可以接受的最小誤差範圍。
-
Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER這兩個常量,用來表示這個範圍的上下限。
-
Number.isSafeInteger()則是用來判斷一個整數是否落在這個範圍之內。
Math對象的拓展
ES6 在 Math 對象上新增了 17 個與數學相關的方法。所有這些方法都是靜態方法,只能在 Math 對象上調用。
-
Math.trunc方法用於去除一個數的小數部分,返回整數部分,對於非數值,Math.trunc內部使用Number方法將其先轉爲數值,對於空值和無法截取整數的值,返回NaN。
-
Math.sign方法用來判斷一個數到底是正數、負數、還是零。對於非數值,會先將其轉換爲數值,如果參數是非數值,會自動轉爲數值。對於那些無法轉爲數值的值,會返回NaN。
-
返回五種值:
參數爲正數,返回+1; 參數爲負數,返回-1; 參數爲 0,返回0; 參數爲-0,返回-0; 其他值,返回NaN。
-
Math.cbrt()方法用於計算一個數的立方根,對於非數值,先使用Number方法將其轉爲數值。
-
Math.clz32()方法將參數轉爲 32 位無符號整數的形式,然後這個 32 位值裏面有多少個前導 0,對於空值或其他類型的值,Math.clz32方法會將它們先轉爲數值,然後再計算。
-
Math.imul()方法返回兩個數以 32 位帶符號整數形式相乘的結果,返回的也是一個 32 位的帶符號整數。
-
Math.fround()方法返回一個數的32位單精度浮點數形式,主要作用,是將64位雙精度浮點數轉爲32位單精度浮點數,對於 NaN 和 Infinity,此方法返回原值。對於其它類型的非數值,Math.fround 方法會先將其轉爲數值,再返回單精度浮點數。
-
Math.hypot()方法返回所有參數的平方和的平方根,如果參數不是數值,Math.hypot方法會將其轉爲數值。只要有一個參數無法轉爲數值,就會返回 NaN。
對數方法
Math.expm1(x)
返回 ex - 1,即Math.exp(x) - 1
。Math.log1p(x)
方法返回1 + x
的自然對數,即Math.log(1 + x)
。如果x
小於-1,返回NaN
。Math.log10(x)
返回以 10 爲底的x
的對數。如果x
小於 0,則返回 NaN。Math.log2(x)
返回以 2 爲底的x
的對數。如果x
小於 0,則返回 NaN。
雙曲函數方法
ES6 新增了 6 個雙曲函數方法。
Math.sinh(x)
返回x
的雙曲正弦(hyperbolic sine)Math.cosh(x)
返回x
的雙曲餘弦(hyperbolic cosine)Math.tanh(x)
返回x
的雙曲正切(hyperbolic tangent)Math.asinh(x)
返回x
的反雙曲正弦(inverse hyperbolic sine)Math.acosh(x)
返回x
的反雙曲餘弦(inverse hyperbolic cosine)Math.atanh(x)
返回x
的反雙曲正切(inverse hyperbolic tangent)
ES2016 新增了一個指數運算符(**
),特點是右結合,而不是常見的左結合。多個指數運算符連用時,是從最右邊開始計算的。
2 ** 2 // 4
2 ** 3 // 8
// 相當於 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
指數運算符可以與等號結合,形成一個新的賦值運算符(**=
)。
let a = 1.5;
a **= 2;
// 等同於 a = a * a;
let b = 4;
b **= 3;
// 等同於 b = b * b * b;