new Function 和 eval 的區別能夠搜索到蠻多解釋,但總以爲還不夠具體,尋思着補補刀html
模板引擎能夠怎樣理解呢? 在一段 Html 文檔裏面有許多佔位符,同時如今還有另外一份 Data 數據,將 Data 注入到 Html 中填充佔位符的方法,就是模板引擎了(簡單得一批)node
既然已經知道目標,那就來簡單實現一個不帶if
、else if
、else
、for
的(要不我說簡易版嘛),模板定義以下:正則表達式
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<p>{{hello + world}}</p>
</body>
</html>
複製代碼
這裏只有一個關鍵點,就是使用 {{}}
雙花括號做爲模板的佔位符,其中能夠讀取 Data 中的變量,也能夠進行一些簡單合法的 Js 表達式,對應的,準備這樣一份 Data:ui
{
"title": "facemagic",
"hello": "hello",
"world": "world"
}
複製代碼
Now,發揮你的想象,如何才能夠將 Data 注入到 Tpl 當中,輸出目標 Html 字符串?注意到,模板佔位符 {{}}
中的內容至少是一個合法的 Js 表達式,使用正則表達式找到全部佔位符裏的表達式,再經過 eval
來執行,完了把執行結果替換掉佔位符,完美!!!來,走一個:spa
// node 環境
// data 解構到 global 下
for (const key in data) {
global[key] = data[key]
}
const regex = /{{([^}]*)}}/g;
const source = source.replace(regex, (m, n) => {
let result = m;
try {
result = eval(n)
} catch (e) {
}
return result;
});
複製代碼
Wait a minute !!! 爲毛要將 data 中的元素所有賦值到 global 下? 注意模板表達式的寫法是 {{title}}
而不是 {{data.title}}
,省略了根索引有木有,爲了執行 eval 不報錯,必須把 data 解構到 global 中。code
這樣就有瑕疵了,若是程序中不當心定義了一個變量恰好跟 data 的某個鍵重名了。那麼可怕的事情將會發生regexp
在上述的狀況中,eval 的方式會形成變量的全局污染,幸運的是,使用 new Function 能夠有效的解決這個問題。奧妙就在於,雖然 new Function 跟 eval 的執行效果相似(前者應該封裝了後者),可是 new Function 是能夠傳參的,是這樣定義的htm
new Function(arg1, arg2, ..., code)
複製代碼
其中,code 能夠直接使用 arg一、arg二、... 例如:模板引擎
const func = new Function(a, b, 'return a+b');
func(1, 2); // 3
複製代碼
基於此,咱們只須要將 data 解構,做爲參數傳入構造好的 func,就不會有全局污染了:索引
function excute(keys, values, statement) {
const caller = new Function(...keys, `return (${statement})`);
return caller(...values);
}
function parse(source, data) {
const keys = Object.keys(data);
const values = [];
for (let i = 0; i < keys.length; i++) {
values.push(data[keys[i]])
}
const regex = /{{([^}]*)}}/g;
source = source.replace(regex, (m, n) => {
let result = m;
try {
result = excute(keys, values, n);
} catch (e) {
}
return result;
})
}
複製代碼
嗯嗯,能夠收工了~~
舉了一個這麼大大咧咧的栗子,其實想表達的不過是,new Function 相比於 eval 能夠傳入參數,能夠有更好的做用域壁壘。
不過,應該看着不無聊...