The starting

Like many Javascript novices, I started by embedding JSON data into HTML in the form of concatenated strings. It starts with a small amount of code and is acceptable for the time being. But as the page structure becomes more complex, its weaknesses become unbearable:

  • Writing is incoherent. Every time you write a variable, you break it off and insert a + and “. It’s easy to make mistakes.
  • Cannot be reused.HTMLFragments are discrete data and it is difficult to extract the repeated parts.
  • Can’t be put to good use<template>The label. This is aHTML5A new TAB in the standard is highly recommendedHTMLThe template in the<template>Tag to make the code more concise.

And I was like, you’re fucking kidding me.

ES6 template string is really convenient to use, for older projects, projects do not have webpack, gulp and other construction tools, can not use ES6 syntax, but want to learn from this excellent way to deal with string concatenation, we can try to write a, mainly for the idea, You can emulate this functionality of ES6 template strings using ES6 syntax.

The back end usually returns JSON data format, so we follow the following rules for simulation.

Requirements describe

Implement a render(template, context) method that fills the placeholders in template with context.

Requirements:

There is no need for control flow components (loops, conditions, etc.), just variable substitution


Cascade variables can also be expanded


Escaped delimiters {and} should not be rendered, and whitespace characters are allowed between delimiters and variables

Var obj = {name:" ",age:" "}; Var STR = "{{name}} {age}} "; Output: February is awesome and only 15.Copy the code

PS: This article needs to be correctRegular expressionHave some understanding, if not yetRegular expression, it is suggested to learn regularity first. Regularity is also a necessary skill for the interview and written test. There are many links to learn regularity at the end of the link above.

How would you do that? You can try to write yourself first, it is not difficult to achieve.

Don’t say my implementation, I put this problem to other friends to do, the implementation is not the same, we first look at the implementation of a few children’s shoes, and then on their basis to find common mistakes and implementation is not elegant.

February children’s shoes:

Let STR = "{{name}}} age "let obj = {name: '2 ', age: 15} function test(str, obj){ let _s = str.replace(/\{\{(\w+)\}\}/g, '$1') let result for(let k in obj) { _s = _s.replace(new RegExp(k, 'g'), obj[k]) } return _s } const s = test(str, obj)Copy the code

The key value of an Object is not necessarily w, and if the string looks like this:

Let STR = "{{name}} very name, only {{age}} "' output: February very bad, only 15 years oldCopy the code

$1 = $1; $1 = $1; $1 = $1; $1 = $1;

  1. The purpose of the code isstr, first match with the re{{name}}{{age}}And then use the grouping to get the parenthesesname.ageIn the end,replaceMethods the{{name}}{{age}}replacenameage, and finally the string becomesMy name is very oldAnd the lastfor inIt’s the loop that causes them all to be replaced.
  2. withfor inThere’s no need for a loopfor inTry not to usefor in.for inIt iterates through all the properties of itself and the prototype chain.

Zhiqin Children’s shoes:

Var STR = "{{name}} {age}} "; Var str2 = "{{name}} "; var str2 = "{{name}} "; Var obj = {name: ' ', age: 15}; function fun(str, obj) { var arr; arr = str.match(/{{[a-zA-Z\d]+}}/g); for(var i=0; i<arr.length; i++){ arr[i] = arr[i].replace(/{{|}}/g,''); str = str.replace('{{'+arr[i]+'}}',obj[arr[i]]); } return str; } console.log(fun(str,obj)); console.log(fun(str2,obj));Copy the code

Idea is correct, know the last to replace is {{name}} and {{age}} as a whole, rather than as last February’s shoes to replace the name, all run no problem for certain, implementation is realized but feel that, we need to discuss is one line of code is the code for as little as possible.

Victoria’s shoes:

function a(str, obj) { var str1 = str; for (var key in obj) { var re = new RegExp("{{" + key + "}}", "g"); str1 = str1.replace(re, obj[key]); } console.log(str1); } const STR = "{{name}} very complete name {{name}}, age to {{age}}"; const obj = { name: "jawil", age: "15" }; a(str, obj);Copy the code

{{key}} obj[key] = value {{key}} obj[key] = value {{key}} obj[key] = value {{key}}

My implementation:

function parseString(str, obj) { Object.keys(obj).forEach(key => { str = str.replace(new RegExp(`{{${key}}}`,'g'), obj[key]); }); return str; } const STR = "{{name}} very complete name {{name}}, age to {{age}}"; const obj = { name: "jawil", age: "15" }; console.log(parseString(str, obj));Copy the code

Actually, there are some problems here. First of all, I didn’t use for… The in loop is to consider unnecessary loops because for… The IN loop iterates through all the enumerable properties of the prototype chain, causing unnecessary loops.

We can take a quick example by looking at for… The fearfulness of in.

// Chrome v63 const div = document.createElement('div'); let m = 0; for (let k in div) { m++; } let n = 0; console.log(m); // 231 console.log(Object.keys(div).length); / / 0Copy the code

A DOM node attribute has so many attributes. This example is just to show the efficiency of for-in traversal. Do not use for-in loop easily.

In addition to using the for loop gain in obj key value, can also use the Object. The key (), Object, getOwnPropertyNames () and Reflect. OwnKeys () also can get, so what is the difference between these? Here are some of the differences.

for... inLoop: iterates through the properties of the object itself, as well as the stereotype properties,
for... inLoops only iterate over enumerable (not included
enumerablefor
falseProperties). like
Array
ObjectObjects created using built-in constructors inherit from
Object.prototype
String.prototypeThe non-enumerable property of

Object.key() : can get its own enumerable attributes, but not the attributes on the prototype chain;

Object. GetOwnPropertyNames () : can get all their properties (including an enumeration), but can not get the properties of the prototype chain Symbols attributes also can not get.

Reflect. OwnKeys: this method is used to return all attributes of the Object, basic is equal to the Object. The getOwnPropertyNames () with the Object. GetOwnPropertySymbols combined.

The above may be more abstract than intuitive. You can watch a DEMO I wrote. It’s all simple.

const parent = { a: 1, b: 2, c: 3 }; const child = { d: 4, e: 5, [Symbol()]: 6 }; child.__proto__ = parent; Object.defineProperty(child, "d", { enumerable: false }); for (var attr in child) { console.log("for... in:", attr); // a,b,c,e } console.log("Object.keys:", Object.keys(child)); // [ 'e' ] console.log("Object.getOwnPropertyNames:", Object.getOwnPropertyNames(child)); // [ 'd', 'e' ] console.log("Reflect.ownKeys:", Reflect.ownKeys(child)); // [ 'd', 'e', Symbol() ]Copy the code

The final implementation

In fact, the above implementation is very simple, but there are still some imperfect places, through MDN first let’s understand the use of replace.

Through document written in STR. Replace (regexp | substr, newSubStr | function), we can find the replace method can be introduced into the function callback function,

Function (replacement) A function that creates a new substring and returns a value that replaces the result of the first argument. Refer to this to specify a function as an argument.

With this sentence, in fact, it is very easy to implement, first look at the specific code and then do the next step analysis.

function render(template, context) { return template.replace(/\{\{(.*?) \}\}/g, (match, key) => context[key]); } const template = "{{name}} {age}} "; const context = { name: "jawil", age: "15" }; console.log(render(template, context));Copy the code

The return value (obj[key]=jawil) replaces the result of the first argument (match=={{name}}).

A brief analysis:.*? Regular fixed collocation is used to mean non-greedy matching pattern, as few matches as possible. Let’s take a simple example.

Let’s start with an example:

Source string: aa<div>test1</div>bb<div>test2</div>cc Regular expression one: <div>.*</div> Matching result one: <div>test1</div>bb<div>test2</div> Regular expression two: <div>.*? <div>test1</div> </div>Copy the code

According to the above example, from the matching behavior analysis, what is greedy and non-greedy matching mode.

Using the greedy model can match to all {{name}}, {{age}}, the above said also to the regular group, group match to is name, which is the function of the second parameter is the key.

So it’s pretty clear what this line of code means, re matches{{name}}, group acquisitionnameAnd then the{{name}}replaceobj[name](jawil).

Of course, there is a small problem, if there is a space will fail to match, like this:

Const template = "{{name}} {age}} ";Copy the code

Trim () : trim() : trim() : trim()) : trim() : trim();

function render(template, context) { return template.replace(/\{\{(.*?) \}\}/g, (match, key) => context[key.trim()]); } const template = "{{name}} {age}} "; const context = { name: "jawil", age: "15" }; console.log(render(template, context));Copy the code

Attach the function to the String’s prototype chain to get the final version

We can even modify the prototype chain to achieve some cool effects:

String.prototype.render = function (context) { return this.replace(/\{\{(.*?) \}\}/g, (match, key) => context[key.trim()]); };Copy the code

If {} is not a number in the middle, then {} itself does not need to be escaped, so ultimately the simplest code is:

String.prototype.render = function (context) { return this.replace(/{{(.*?) }}/g, (match, key) => context[key.trim()]); };Copy the code

After that, we can call:

"{{name}} is folded the name, age to {{age}}". The render ({name: "jawil", the age: "15"});Copy the code

harvest

Through the realization of a small template string, I realized that it is not difficult to realize a function, but it is even more difficult to achieve perfection. It is necessary to grasp the foundation firmly, have certain precipitation, and then continue to polish to achieve more elegant, through a very small point can often expand a lot of knowledge points.

A quick start with regular expressions: