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.
HTML
Fragments are discrete data and it is difficult to extract the repeated parts. - Can’t be put to good use
<template>
The label. This is aHTML5
A new TAB in the standard is highly recommendedHTML
The 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;
- The purpose of the code is
str
, first match with the re{{name}}
和{{age}}
And then use the grouping to get the parenthesesname
.age
In the end,replace
Methods the{{name}}
和{{age}}
replacename
和age
, and finally the string becomesMy name is very oldAnd the lastfor in
It’s the loop that causes them all to be replaced. - with
for in
There’s no need for a loopfor in
Try not to usefor in
.for in
It 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... in
Loop: iterates through the properties of the object itself, as well as the stereotype properties,
for... in
Loops only iterate over enumerable (not included
enumerable
for
false
Properties). like
Array
和
Object
Objects created using built-in constructors inherit from
Object.prototype
和
String.prototype
The non-enumerable property ofObject.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 acquisitionname
And 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.