Creating a Simple JavaScript Template Engine: A Comprehensive Guide
Written on
Understanding Template Engines
Even seasoned developers may struggle with certain interview questions. One such challenge arose when my friend Nancy faced a tough question during a job interview: she was tasked with creating a JavaScript template engine on the spot.
The request was particularly daunting as she was simply seeking employment. To clarify the question, she was instructed to add a render(obj) method to the String object that substitutes specific placeholders in a string with values from the provided object.
const template = 'My name is ${name}, age ${age}, I am a ${job.name}';
const employee = {
name: 'fatfish',
age: 100,
job: {
name: 'front end development'}
};
const renderStr = template.render(employee);
console.log(renderStr); // Output: 'My name is fatfish, age 100, I am a front end development'
What Exactly is a Template Engine?
You may be familiar with template engines such as Nunjucks, which serve a similar purpose. Let’s explore an example using Nunjucks:
nunjucks.configure({ autoescape: true });
const template = 'My name is {{name}}, age {{age}}, I am a {{job.name}}';
const employee = {
name: 'fatfish',
age: 100,
job: {
name: 'front end development'}
};
const renderStr = nunjucks.renderString(template, employee);
console.log(renderStr); // Output: 'My name is fatfish, age 100, I am a front end development'
Nancy's challenge was to implement something akin to this functionality, transitioning from ${name} to {{name}}.
Solution 1: Using Regular Expressions
My initial instinct was to leverage regular expressions for this task. By extracting variables from the string, we could simplify the problem.
Step 1: Extract Variables
String.prototype.render = function (obj) {
const template = this;
const variableRegex = /${([^${}]+)}/g;
template.replace(variableRegex, ($0, variable) => {
console.log(variable);});
};
const template = 'My name is ${name}, age ${age}, I am a ${job.name}';
template.render();
This code captures the variables name, age, and job.name. The regex ([^{]+) focuses on capturing any characters except for $, {, or }.
Step 2: Retrieve Values from the Object
String.prototype.render = function (obj) {
const template = this;
const variableRegex = /${([^${}]+)}/g;
const getVariableValue = (variable) => {
variable = variable.split('.');
let variableValue = obj;
while (variable.length) {
variableValue = variableValue[variable.shift()];}
return variableValue;
};
const renderStr = template.replace(variableRegex, ($0, variable) => {
return getVariableValue(variable);});
return renderStr;
};
const template = 'My name is ${name}, age ${age}, I am a ${job.name}';
const employee = {
name: 'fatfish',
age: 100,
job: {
name: 'front end development'}
};
const renderStr = template.render(employee);
console.log(renderStr); // Output: 'My name is fatfish, age 100, I am a front end development'
We've successfully created a simple template engine using regular expressions!
Solution 2: Utilizing eval
Let's review how template literals in ES6 work:
const name = 'fatfish';
const age = 100;
const job = {
name: 'front end development'
};
const renderString = My name is ${name}, age ${age}, I am a ${job.name};
console.log(renderString);
With eval, we can dynamically declare variables:
const employee = {
name: 'fatfish',
age: 100,
job: {
name: 'front end development'}
};
eval('var { name, age, job } = employee');
console.log(name, age, job);
Combining these concepts, we can craft our second solution:
String.prototype.render = function (obj) {
const template = this;
eval(var { ${Object.keys(obj).join(',')} } = obj);
const renderStr = eval('`' + template + '`');
return renderStr;
};
const template = 'My name is ${name}, age ${age}, I am a ${job.name}';
const employee = {
name: 'fatfish',
age: 100,
job: {
name: 'front end development'}
};
const renderStr = template.render(employee);
console.log(renderStr);
Solution 3: Using with
The with statement can also simplify our task:
const employee = {
name: 'fatfish',
age: 100,
job: {
name: 'front end development'}
};
with (employee) {
console.log(name, age, job);
}
This approach is more concise, achieving similar results:
String.prototype.render = function (obj) {
with (obj) {
return eval('`' + this + '`');}
};
const template = 'My name is ${name}, age ${age}, I am a ${job.name}';
const employee = {
name: 'fatfish',
age: 100,
job: {
name: 'front end development'}
};
const renderStr = template.render(employee);
console.log(renderStr);
Conclusion
Thank you for taking the time to read this guide! I hope you found it enlightening and are eager to explore more high-quality content in the future.
For more engaging discussions, be sure to follow us on various platforms and join our community!