Разнице между new Function и eval можно найти множество объяснений, но я всегда чувствую, что она недостаточно конкретна, думая о том, чтобы сделать нож.
1. Начните с простого шаблонизатора
Как шаблонизатор может это понять? В документе Html есть много заполнителей, и теперь есть другие данные Data, метод внедрения данных в Html для заполнения заполнителей - это механизм шаблонов (простой пакет)
Теперь, когда вы знаете цель, давайте просто реализуем ее безif
,else if
,else
,for
(или я должен сказать упрощенную версию), шаблон определяется следующим образом:
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<p>{{hello + world}}</p>
</body>
</html>
Здесь есть только один ключевой момент, который заключается в использовании{{}}
Двойные фигурные скобки используются как заполнители для шаблонов, в которых можно читать переменные в Data, а также выполнять некоторые простые и допустимые Js-выражения, соответственно подготавливаем такие Data:
{
"title": "facemagic",
"hello": "hello",
"world": "world"
}
Теперь используйте свое воображение, как вы можете ввести данные в Tpl и вывести целевую строку Html? Обратите внимание, что заполнители шаблонов{{}}
Содержимое является как минимум допустимым выражением Js, используйте регулярные выражения, чтобы найти выражения во всех заполнителях, а затем передайтеeval
Чтобы выполнить, когда закончите, замените заполнитель результатом выполнения, отлично! ! ! Давай, иди один:
// 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;
});
Подождите минуту! ! !
Назначить все элементы данных глобальными для Мао?
Обратите внимание, что выражение шаблона записывается как{{title}}
вместо{{data.title}}
, опуская корневой индекс, и чтобы выполнить eval без сообщения об ошибке, данные должны быть деконструированы в глобальные.
Это ошибочно, если программа случайно определяет переменную с тем же именем, что и ключ данных. Тогда будут происходить ужасные вещи
2. Используйте новую функцию
В приведенном выше случае метод eval вызовет глобальное загрязнение переменных, К счастью, использование новой функции может эффективно решить эту проблему. Секрет в том, что хотя эффект выполнения новой функции аналогичен эффекту eval (первая должна инкапсулировать вторую), новая функция может передавать параметры, которые определены следующим образом.
new Function(arg1, arg2, ..., code)
Среди них код может напрямую использовать arg1, arg2,... Например:
const func = new Function(a, b, 'return a+b');
func(1, 2); // 3
Исходя из этого, нам нужно только деструктурировать данные и передать их в построенную функцию в качестве параметра, и глобального загрязнения не будет:
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;
})
}
Мммм, пора на работу~
3. Резюме
С таким беззаботным каштаном я действительно хочу выразить то, что новая функция может передавать параметры по сравнению с eval и может иметь лучшие барьеры области видимости.
Но это не должно выглядеть скучно...