JavaScript进阶进修(一)—— 基于正则表达式的简朴js模板引擎完成

文章泉源:小青年原创
宣布时刻:2016-06-26
关键词:JavaScript,正则表达式,js模板引擎
转载需标注本文原始地点: http://zhaomenghuan.github.io…

媒介

这年头MVC、MVVM框架众多,许多时刻我们只是用了这些框架,有时刻想深切去相识这些框架背地的道理完成时,浏览源码时发明无从下手,js基础功几乎渣渣,所以想运用业余时刻照样要补补基础。之前看JavaScript的一些书本时老是把正则表达式这一章跳过了,碰到一些须要写正则的时刻然后都是种种copy,js要进阶以为照样要系统进修一下正则,虽然看起来像乱码一样的婚配划定规矩,然则假如闇练运用会很有用,那末本日就先从正则最先吧!和大部份书本一样,本文前篇也会是解说基础,本文的许多内容都是摘自收集举行整顿,有些内容须要列位本身举行实践考证。

正则表达式

正则表达式(英语:Regular Expression,在代码中常简写为regex、regexp或RE)运用单个字符串来形貌、婚配一系列相符某个句法划定规矩的字符串搜刮情势。搜刮情势可用于文本搜刮和文本替换。

基础语法

RegExp 组织函数可建立一个正则表达式对象,用特定的情势婚配文本。建立一个正则对象的两种要领:字面量和组织函数。要示意字符串,字面量情势不运用引号,而传递给组织函数的参数运用引号。

字面量: /pattern/flags
组织函数: RegExp(pattern [, flags])
  • pattern 正则表达式文本

  • flags 该参数可所以下面单个值或许几个值的恣意组合:

    • g 全局婚配

    • i 疏忽大小写

    • gi或ig 全局婚配、疏忽大小写

    • m 多行查找,让最先和完毕字符(^ 和 $)事变在多行情势(也就是,^ 和 $ 可以婚配字符串中每一行的最先和完毕(行是由 n 或 r 支解的),而不只是全部输入字符串的最最先和最末端处。

    • u Unicode。将情势视为Unicode码位(code points)序列。

    • y sticky。运用隐式的^锚点把正则锚定在了lastIndex所指定的偏移位置处,详细可以看看这篇文章:JavaScript:正则表达式的/y标识

详细例子:

/ab+c/i;
new RegExp('ab+c', 'i');
new RegExp(/ab+c/, 'i');

当表达式被赋值时,字面量情势供应正则表达式的编译(compilation)状况,当正则表达式坚持为常量时运用字面量。比方当你在轮回中运用字面量组织一个正则表达式时,正则表达式不会在每一次迭代中都被从新编译(recompiled)。

而正则表达式对象的组织函数,如 new RegExp(‘ab+c’) 供应了正则表达式运行时编译(runtime compilation)。假如你晓得正则表达式情势将会转变,或许你事前不晓得什么情势,而是从另一个泉源猎取,如用户输入,这些状况都可以运用组织函数。

从ECMAScript 6最先,当第一个参数为正则表达式而第二个标志参数存在时,new RegExp(/ab+c/, ‘i’)不再抛出TypeError (“当从其他正则表达式举行组织时不支撑标志”)的非常,取而代之,将运用这些参数建立一个新的正则表达式。

当运用组织函数制作正则对象时,须要通例的字符转义划定规矩(在前面加反斜杠 )。比方,以下是等价的:

var re = new RegExp("\\w+");
var re = /\w+/;

正则表达式中的特别字符

^$ —— 作用是离别指出一个字符串的最先和完毕。

  • “^The”:示意一切以”The”最先的字符串”There”,”The cat”等;

  • “of despair$”:示意所以以”of despair”末端的字符串;

  • “^abc$”:示意最先和末端都是”abc”的字符串——呵呵,只要”abc”本身了;

  • “notice”:示意任何包含”notice”的字符串。

末了谁人例子,假如你不运用两个特别字符,你就在示意要查找的串在被查找串的恣意部份——你并不把它定位在某一个顶端。

*+?{} —— 示意一个或一序列字符反复涌现的次数。

离别示意“没有或更多”,“一次或更多”,“没有或一次”和指定反复次数的局限。{}必须指定局限的下限,如:”{0,2}”而不是”{,2}”。*、+和?相当于{0,}、{1,}和{0,1}。

  • “ab*”:示意一个字符串有一个a背面随着零个或若干个b。(”a”, “ab”, “abbb”,……);

  • “ab+”:示意一个字符串有一个a背面随着最少一个b或许更多;

  • “ab?”:示意一个字符串有一个a背面随着零个或许一个b;

  • “a?b+$”:示意在字符串的末端有零个或一个a随着一个或几个b。

  • “ab{2}”:示意一个字符串有一个a随着2个b(”abb”);

  • “ab{2,}”:示意一个字符串有一个a随着最少2个b;

  • “ab{3,5}”:示意一个字符串有一个a随着3到5个b。

| —— 示意“或”操纵

  • “hi|hello”:示意一个字符串里有”hi”或许”hello”;

  • “(b|cd)ef”:示意”bef”或”cdef”;

  • “(a|b)*c”:示意一串”a””b”夹杂的字符串背面跟一个”c”;

. —— 可以替换任何字符

  • “a.[0-9]”:示意一个字符串有一个”a”背面随着一个恣意字符和一个数字;

  • “^.{3}$”:示意有恣意三个字符的字符串(长度为3个字符);

[] —— 示意某些字符许可在一个字符串中的某一特定位置涌现

  • “[ab]”:示意一个字符串有一个”a”或”b”(相当于”a|b”);

  • “[a-d]”:示意一个字符串包含小写的’a’到’d’中的一个(相当于”a|b|c|d”或许”[abcd]”);

  • “^[a-zA-Z]”:示意一个以字母开首的字符串;

  • “[0-9]%”:示意一个百分号前有一名的数字;

  • “,[a-zA-Z0-9]$”:示意一个字符串以一个逗号背面随着一个字母或数字完毕。

可以在方括号里用’^’示意不愿望涌现的字符,'^'应在方括号里的第一名。(如:%[^a-zA-Z]%示意两个百分号中不应当涌现字母)。为了逐字表达,你必须在^.$()|*+?{\这些字符前加上转移字符”。在方括号中,不须要转义字符。

RegExp对象的属性

属性寄义
$1-$9假如它(们)存在,是婚配到的子串
$_拜见input
$*拜见multiline
$&拜见lastMatch
$+拜见lastParen
$`拜见leftContext
$”        拜见rightContext
constructor   建立一个对象的一个特别的函数原型
global      是不是在全部串中婚配(bool型)
ignoreCase    婚配时是不是疏忽大小写(bool型)
input       被婚配的串
lastIndex    末了一次婚配的索引
lastParen    末了一个括号括起来的子串
leftContext   近来一次婚配以左的子串
multiline    是不是举行多行婚配(bool型)
prototype    许可附加属性给对象
rightContext   近来一次婚配以右的子串
source      正则表达式情势
lastIndex    末了一次婚配的索引

概况人人可以检察这里:MDN JavaScript 规范库 RegExp属性

RegExp对象的要领

要领寄义
compile   正则表达式比较
exec       实行查找
test       举行婚配
toSource    返回特定对象的定义(literal representing),其值可用来建立一个新的对象。重载Object.toSource要领获得的。
toString      返回特定对象的串。重载Object.toString要领获得的。
valueOf    返回特定对象的原始值。重载Object.valueOf要领获得

概况人人可以检察这里:MDN JavaScript 规范库 RegExp要领

不过在这里我们照样须要申明一下的是我们用得比较多的要领主要分为两类:

RegExp 对象要领

RegExp.prototype.compile() ——编译正则表达式

用法:regexObj.compile(pattern, flags)

功用申明:compile() 要领用于在剧本实行过程当中编译正则表达式,也可用于转变和从新编译正则表达式。该要领可以编译指定的正则表达式,编译以后的正则表达式实行速率将会进步,假如正则表达式屡次被挪用,那末挪用compile要领可以有用的进步代码的实行速率,假如该正则表达式只能被运用一次,则不会有显著的效果。

var str="Every man in the world! Every woman on earth!";
var patt=/man/g;
var str2=str.replace(patt,"person");
document.write(str2+"<br>");
patt=/(wo)?man/g;
patt.compile(patt); 
str2=str.replace(patt,"person");
document.write(str2);
效果:
Every person in the world! Every woperson on earth!
Every person in the world! Every person on earth!

RegExp.prototype.exec() —— 检索字符串中指定的值。返回找到的值,并肯定其位置。

用法:regexObj.exec(str)

功用申明:exec() 要领假如胜利婚配,exec 要领返回一个数组,而且更新正则表达式对象的属性。返回的数组包含婚配的字符串作为第一个元素,紧接着一个元素对应一个胜利婚配被捕捉的字符串的捕捉括号(capturing parenthesis)。(one item for each capturing parenthesis that matched containing the text that was captured.)假如婚配失利,exec 要领将返回 null。

var str="Hello world,hello zhaomenghuan!";
// look for "Hello"或"hello"
var patt=/hello/gi;
while((result = patt.exec(str))!== null){
    document.write("result:" + result +"的位置为"+ result.index + "<br />"); 
}
效果:
result:Hello的位置为0
result:hello的位置为12

RegExp.prototype.test() —— 检索字符串中指定的值。返回 true 或 false。

用法:regexObj.test(str)

功用申明:test() 要领用于检测一个字符串是不是婚配某个情势,假如字符串中有婚配的值返回 true ,不然返回 false。

var result = /hello/.test('This is a hello world!');
document.write(result);
效果:
true

支撑正则表达式的 String 对象的要领

search —— 检索与正则表达式相婚配的值

用法:string.search(searchvalue)

searchvalue 必须。查找的字符串或许正则表达式。

功用申明:search()要领用于检索字符串中指定的子字符串,或检索与正则表达式相婚配的子字符串。假如没有找到任何婚配的子串,则返回 -1。

var str="Mr. Blue has a blue house";
document.write(str.search(/blue/i));
效果:
4

match —— 找到一个或多个正则表达式的婚配。

用法:string.match(regexp)

regexp 必须。划定要婚配的情势的 RegExp 对象。假如该参数不是 RegExp 对象,则须要起首把它传递给 RegExp 组织函数,将其转换为 RegExp 对象。返回值范例为Array。

功用申明:match() 要领可在字符串内检索指定的值,或找到一个或多个正则表达式的婚配。match() 要领将检索字符串 String Object,以找到一个或多个与 regexp 婚配的文本。这个要领的行动在很大水平上有赖于 regexp 是不是具有标志 g。假如 regexp 没有标志 g,那末 match() 要领就只能在 stringObject 中实行一次婚配。假如没有找到任何婚配的文本, match() 将返回 null。不然,它将返回一个数组,个中存放了与它找到的婚配文本有关的信息。

var str = "The rain in SPAIN stays mainly in the plain"; 
var match=str.match(/ain/gi);
document.getElementById("demo").innerHTML=match;
效果:
ain,AIN,ain,ain

replace 替换与正则表达式婚配的子串。

用法:string.replace(searchvalue,newvalue)

searchvalue 必须。划定子字符串或要替换的情势的 RegExp 对象。
请注意,假如该值是一个字符串,则将它作为要检索的直接量文本情势,而不是起首被转换为 RegExp 对象。
newvalue 必须。一个字符串值。划定了替换文本或天生替换文本的函数。
返回值为String范例,一个新的字符串,是用 replacement 替换了 regexp 的第一次婚配或一切婚配以后获得的。

split 把字符串支解为字符串数组。

用法:string.split(separator,limit)

separator 可选。字符串或正则表达式,从该参数指定的处所支解 string Object。
limit 可选。该参数可指定返回的数组的最大长度。假如设置了该参数,返回的子串不会多于这个参数指定的数组。假如没有设置该参数,全部字符串都会被支解,不斟酌它的长度。
返回值为Array范例,一个字符串数组。该数组是经由过程在 separator 指定的边境处将字符串 string Object 支解成子串建立的。返回的数组中的字串不包含 separator 本身。

var str="How are you doing today?";
var match = str.split(/ /);
document.write(match);
效果:
How,are,you,doing,today?

正则表达式的运用实例申明

校验是不是全由数字构成 —— /^[0-9]{1,20}$/

^ 示意打头的字符要婚配紧跟^背面的划定规矩
&dollar; 示意打头的字符要婚配紧靠$前面的划定规矩
[ ] 中的内容是可选字符集
[0-9] 示意请求字符局限在0-9之间
{1,20}示意数字字符串长度正当为1到20,即为[0-9]中的字符涌现次数的局限是1到20次。

/^ 和 $/成对运用应当是示意请求全部字符串完全婚配定义的划定规矩,而不是只婚配字符串中的一个子串。

校验登录名:只能输入5-20个以字母开首、可带数字、“_”、“.”的字串—— /^[a-zA-Z]{1}([a-zA-Z0-9]|[._]){4,19}$/

^[a-zA-Z]{1} 示意第一个字符请求是字母。
([a-zA-Z0-9]|[._]){4,19} 示意从第二位最先(由于它紧跟在上个表达式背面)的一个长度为4到9位的字符串,它请求是由大小写字母、数字或许特别字符集[._]构成。

校验暗码:只能输入6-20个字母、数字、下划线——/^(w){6,20}$/

w:用于婚配字母,数字或下划线字符

校验一般电话、传真号码:可以“+”或数字开首,可含有“-” 和 “ ”——/^[+]{0,1}(d){1,3}[ ]?([-]?((d)|[ ]){1,12})+$/

d:用于婚配从0到9的数字;
“?”元字符划定其前导对象必须在目标对象中一连涌现零次或一次
可以婚配的字符串如:+123 -999 999 ; +123-999 999 ;123 999 999 ;+123 999999等

校验URL——/^http[s]{0,1}://.+&dollar;/ 或 /^http[s]{0,1}://.{1,n}$/ (示意url串的长度为length(“https://”) + n )

/ :示意字符“/”。
. 示意一切字符的集
+ 等同于{1,},就是1到正无限吧。

校验纯中文字符——/^[u4E00-u9FA5]+$/

[u4E00-u9FA5] :中文字符集的局限

以上表达式均在下面的javascript中测试经由过程:

<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
</head>
<body>
    <form>
        划定规矩表达式 : (填写/ /之间的表达式)<br />
        <input type="input" name="regxStr" value="" ><br />
        校验字符串 :<br />
        <input type="input" name="str" value="" >
        <input type="button" name="match" value="婚配" onClick="alert(regx(regxStr.value,str.value));">
    </form>
    <script type="text/javascript">
        function regx(r,s){
            if (r == null || r == ""){
               return;
            }
            var patrn= new RegExp(r);
            return patrn.test(s)
        }
    </script>
</body>
</html>

js模板引擎完成道理

前面我们花了很长的篇幅解说正则表达式是为了人人看这部份是时刻可以有所邃晓,假如前面的内容一会儿没有看懂也没有关联,人人可以先看看这部份的内容回过甚去检察方才的内容。

我们起首会想到写一个模板,我们罕见的是写成如许:

<script type="text/html" id="template">
    <p>name: {{name}}</p>
    <p>age: {{profile.age}}</p>
</script>

固然也可以运用<template></template>标签,而且这个也是如今的盛行趋向,拥抱模块化,不过本文不是讲这个标签和模块化,假如人人感兴趣可以看看这两篇文章:

人人也可以看下面这个基础例子:

<div class="content"></div>    

<template id="template">
    <p>name: 小青年</p>
    <p>age: 22</p>
</template>    

<script type="text/javascript">    
    var isSupport='content' in document.createElement('template');
    if (isSupport) {    
        var tpl = document.querySelector('#template');
        var content = document.querySelector(".content");
        var clone = document.importNode(tpl.content, true);
        content.appendChild(clone); 
    } else {
        alert("is not support template");
    }
</script>

回归正题,我们继承说上面的模板,最先写模板引擎。

基础模板引擎道邃晓说

我们运用js模板引擎,可以认为是在做一个MVC构造的系统,模子(model)-视图(view)-控制器(controller)。控制器(controller)作为中心部份,起首要拿到模子,这里我们须要拿到模板内里与视图相干的内容,如上面的例子中{{ }}中的内容,起首用正则查找:

var re = /{{(.+?)}}/g;
while((match = re.exec(tpl))!==null) {
     console.log(match);
}
效果:
"{{name}},name"
"{{age}},age"

/{{(.+?)}}/g的意义是查找开首为{{和末端为}}的子字符串。经由过程RegExp 对象exec()要领搜刮婚配获得的是一个数组,我们可以经由过程match[0]示意婚配的原字符串,match[1]示意婚配的目标字符串,我们经由过程实行字符串替换要领就可以获得目标字符串。

<div id="content"></div>
<!--HTML模板(相似MVC中的view)-->
<script type="text/html" id="template">
    <p>name: {{name}}</p>
    <p>age: {{age}}</p>
</script>
        
<script type="text/javascript">
    // 模板引擎函数(相似MVC中的controller)
    var mtpl = function(tpl, data) {
        var re = /{{(.+?)}}/g;
        while((match = re.exec(tpl))!==null) {
            if(match[1]){
                tpl = tpl.replace(match[0], data[match[1]])
            }else{
                tpl = tpl.replace(match[0], '')
            }    
        }
        return tpl
    }
    
    // 模板数据(相似MVC中的model)
    var tpl = document.getElementById("template").innerHTML;
    document.getElementById("content").innerHTML = mtpl(tpl,{
        name: "zhaomenghuan",
        age: 22
    });
</script>

这里我们经由过程data[‘key’]的情势取值然后替换模板中的{{…}}的内容完成了一个内容的替换。上述代码很简朴,基础完成了一个字符替换罢了,我们上面是经由过程字符串替换完成了模板和数据的婚配,然则假如我们上面谁人json数据是如许的:

var data = {
    base: {
        name: 'zhaomenghuan',
        age: 22    
    },
    skills: ['html5','javascript','android']
}

我们直接经由过程data[match[1]]举行明显会有题目,我们虽然可以经由过程data.base[‘name’]猎取,然则关于模板引擎函数封装来讲是不够完美的,而且也不能实行JavaScript,彷佛并没有相似于一些著名的js模板引擎库中的语法功用,所以略显low。下面我们在这个基础上举行革新。

下面我们说一下一种最原始的要领,经由过程Function组织器完成,依据字符串建立一个函数。在一篇文章中看到说这类要领实行JavaScript机能低下,然则关于初学者来讲,进修一下完成思绪我以为也是有意义的,毕竟关于新手来讲谈机能是件奢靡的事。我们起首看个例子:

var fn = new Function("arg", "console.log(arg + 1);"); 
fn(2); // outputs 3

fn但是一个名副实在的函数,它接收一个参数,函数体是console.log(arg + 1);,上面谁人例子等同于:

var fn = function(arg) {
    console.log(arg + 1);
}
fn(2);

我们经由过程new Function可以将字符串转成JavaScript实行,看起来是不是是很すごい(凶猛,’sigoyi’,彷佛另有个单词是‘daijobu’,女朋友每次问我,我每次都是回复‘daijo’,女朋友喜好看动漫,本日她放假先回家了,舍不得,想一想她日常平凡萌萌哒的模样,越是缅怀,学几个简朴单词,下次晤面说说,哈哈。)

接着说,我们有时刻参数是多个,我们虽然可以输入多个参数,然则这明显不是最好的方法,我们可以运用apply,如许我们没必要显式地传参数给这个函数。比方我们前面的例子:

var data = {
    name: 'zhaomenghuan',
    age: 22
}
new Function("console.log(this.name + ' is ' + this.age +' years old.');").apply(data);
效果:
"zhaomenghuan is 22 years old."

apply()会自动设定函数实行的上下文,这就是为何我们能在函数内里运用this.name,这里this指向data对象。这里我们获得什么启发呢?我们磨练经由过程给new Function传入模板字符串和数据天生我们的内容。

我们可以经由过程数组push()或许+=拼接体式格局将分开的字符串衔接起来,有文章中称,“在当代浏览器运用+=会比数组push要领快,在v8引擎中运用+=要领会比数组拼接快4.7倍,而在IE6-8下push比+=拼接快”。至于两者效力比较不在本文局限内,人人可以自行探讨,然则我们为了简化题目,不斟酌效力题目,我们可以将分开的字符串用以下要领push拼接:

var r=[];
r.push("<p>");
r.push(this.name);
r.push("</p><p>");
r.push(this.age);
r.push("</p>");
return r.join("");

我们假如直接拼接成数组然后转成对象也可以,然则须要将<>转义,为了轻易,我们有时刻可以如许处置惩罚:

var data = {
    name: 'zhaomenghuan',
    age: 22
}    
var code = 'var r=[];\n';
code += 'r.push("<p>");\n';
code += 'r.push(this.name);\n'
code += 'r.push("</p><p>");\n';
code += 'r.push(this.age);\n';
code += 'r.push("</p>");\n';
code += 'return r.join("");';
console.log(code)
var fn = new Function(code.replace(/[\r\t\n]/g, '')).apply(data);
console.log(fn)
效果:
"<p>zhaomenghuan</p><p>22</p>"

写到这里我置信智慧的人应当晓得我接下来要做的事变了,主如果两个:怎样依据我们自定义的分开字符分开模板字符串,然后就是动态天生字符串。不过我们可以看出来这里我们另有个没有提到的是让模板可以嵌入JavaScript的语法关键词,比方if,for等,这个处置惩罚要领和上面的略有差别,须要加以推断,不过我们可以划分为剖析html和js两大类。

自定义分开字符分开模板字符

在讲怎样支解字符串前我们先看三个函数:slice|splice|split

哈哈,是不是是懵逼了?横竖我经常是晕的,不可,照样要对照一下搞清楚。

经常使用函数 slice | splice | split 对照

String.prototype.slice() —— 从一个字符串中提取字符串并返回新字符串

语法:str.slice(beginSlice[, endSlice])

参数:

  • beginSlice:从该索引(以 0 为基数)处最先提取原字符串中的字符。假如值为负数,会被当作 sourceLength + beginSlice 对待,这里的sourceLength 是字符串的长度 (比方, 假如beginSlice 是 -3 则看做是: sourceLength – 3)

  • endSlice:可选。在该索引(以 0 为基数)处完毕提取字符串。假如省略该参数,slice会一向提取到字符串末端。假如该参数为负数,则被看做是 sourceLength + endSlice,这里的 sourceLength 就是字符串的长度(比方,假如 endSlice 是 -3,则是, sourceLength – 3)。

Array.prototype.slice()—— 把数组中一部份的浅复制(shallow copy)存入一个新的数组对象中,并返回这个新的数组。

语法:arr.slice([begin[, end]])

参数:

  • begin:从该索引处最先提取原数组中的元素(从0最先)。
    假如该参数为负数,则示意从原数组中的倒数第几个元素最先提取,slice(-2)示意提取原数组中的倒数第二个元素到末了一个元素(包含末了一个元素)。假如省略 begin,则 slice 从索引 0 最先。

  • end:在该索引处完毕提取原数组元素(从0最先)。slice会提取原数组中索引从 begin 到 end 的一切元素(包含begin,但不包含end)。
    slice(1,4) 提取原数组中的第二个元素最先直到第四个元素的一切元素 (索引为 1, 2, 3的元素)。假如该参数为负数, 则它示意在原数组中的倒数第几个元素完毕抽取。 slice(-2,-1)示意抽取了原数组中的倒数第二个元素到末了一个元素(不包含末了一个元素,也就是只要倒数第二个元素)。假如 end 被省略,则slice 会一向提取到原数组末端。

形貌:
slice 不修正原数组,只会返回一个包含了原数组中提取的部份元素的一个新数组。原数组的元素会根据下述划定规矩拷贝(“一级深拷贝”[one level deep]划定规矩):

  • 假如该元素是个对象援用 (不是现实的对象),slice 会拷贝这个对象援用到新的数组里。两个对象援用都援用了同一个对象。假如被援用的对象发作转变,则转变将反应到新的和本来的数组中。

  • 关于字符串和数字来讲(不是 String 和 Number 对象),slice 会拷贝字符串和数字到新的数组里。在一个数组里修正这些字符串或数字,不会影响另一个数组。

假如向两个数组任一中增加了新元素,则另一个不会受到影响。

Array.prototype.splice() —— 用新元素替换旧元素,以此修正数组的内容

语法:array.splice(start, deleteCount[, item1[, item2[, …]]])

参数:

  • start​
    从数组的哪一名最先修正内容。假如超出了数组的长度,则从数组末端最先增加内容;假如是负值,则示意从数组末位最先的第几位。

  • deleteCount
    整数,示意要移除的数组元素的个数。假如 deleteCount 是 0,则不移除元素。这类状况下,最少应增加一个新元素。假如 deleteCount 大于start 以后的元素的总数,则从 start 背面的元素都将被删除(含第 start 位)。

  • itemN
    要增加进数组的元素。假如不指定,则 splice() 只删除数组元素。

返回值:
由被删除的元素构成的一个数组。假如只删除了一个元素,则返回只包含一个元素的数组。假如没有删除元素,则返回空数组。
形貌:
假如增加进数组的元素个数不等于被删除的元素个数,数组的长度会发作响应的转变。请注意,splice() 要领与 slice() 要领的作用是差别的,splice() 要领会直接对数组举行修正。

String.prototype.split() —— 经由过程把字符串支解成子字符串来把一个 String 对象支解成一个字符串数组

语法:string.split(separator,limit)

我们在前面解说【支撑正则表达式的 String 对象的要领】时讲到这个要领了,这里不再赘述,列出来只为轻易人人对照进修。

这里列出的要领中关于我们分开字符串有用的是String.prototype.slice()String.prototype.split(),另个要领的区分在于运用slice()要领们须要晓得子字符串的索引值index,运用split()要领我们须要子字符串的内容或许相符的正则表达式。很显著我们的思绪就出来了,接下来我们用代码完成。

基于String.prototype.slice()要领分开字符串

我们这里参考前面的内容写一个基础函数,设置一个变量cursor作为索引值指针,每次实行完一个婚配操纵,我们将指针挪动一下。我们前面讲RegExp.prototype.exec()返回值时重点说到了三个参数。
若match = re.exec(str),则有:
match.index:婚配的对象肇端位置
match[0]:示意婚配的原字符串
match[1]:示意婚配的目标字符串
若我们邃晓了思绪我们就可以写下面的代码:

<script type="text/html" id="template">
    <p>name: {{this.name}}</p>
    <p>age: {{this.age}}</p>
</script>

<script type="text/javascript">    
    var mtpl = function(tpl,data) {
        var re = /{{(.+?)}}/g,cursor = 0;
                            
        while((match = re.exec(tpl))!== null) {
            // 最先标签  {{ 前的内容和完毕标签 }} 后的内容
            console.log(tpl.slice(cursor, match.index))
            // 最先标签  {{ 和 完毕标签 }} 之间的内容
            console.log(match[1])
            // 每一次婚配完成挪动指针
            cursor = match.index + match[0].length;
        }
        // 末了一次婚配完的内容
        console.log(tpl.substr(cursor, tpl.length - cursor))
    }
    
    // 挪用
    var tpl = document.getElementById("template").innerHTML;
    mtpl(tpl,null);
</script>

经由过程上面的代码我们已完成了将模板字符串分开成子字符串。

基于String.prototype.split()要领分开字符串

运用字符串split()要领,下面我们不运用正则作为分开标记,这里就运用自定义标记作为分开规范,如:

var sTag = '{%';//最先标签
var eTag = '%}';//完毕标签

然后我们以sTag为分开符实行split要领:

var matchs = tpl.split(sTag);

返回值matchs 为一个子字符串数组,然后对数组每个子字符串以eTag为分开符实行split要领,进一步获得的子字符串数组分为两种范例,一种是于我们婚配子字符串参数有关的子串数组,一种是与婚配参数无关的子串数组(这类数组长度为1)。之所以要分得这么细是为了背面字符串衔接时更轻易的时刻适宜的要领衔接。

<script type="text/html" id="template">
    <p>name: {%this.name%}</p>
    <p>age: {%this.age%}</p>
</script>

<script type="text/javascript">
    var mtpl = function(tpl, data) {
        var sTag = '{%';//最先标签
        var eTag = '%}';//完毕标签
        var matchs = tpl.split(sTag);
        for (var i = 0, len = matchs.length; i < len; i++) {
            var match = matchs[i].split(eTag);
            if (match.length === 1) {
                console.log(match[0]);
            } else {
                if(match[0]){
                    console.log(match[0]);
                }   
                if (match[1]) {
                    console.log(match[1]);
                }
            }
        }
    }
    
    // 挪用
    var tpl = document.getElementById("template").innerHTML;
    mtpl(tpl,null);
</script>

动态衔接字符串

我们上面运用了字符串分开函数把字符串举行了分开,而且提取了关键子字符串,下面我们解说怎样将这些字符串衔接起来。
我们这里定义一个如许的模板:

<script type="text/tpl" id="template">
    <p>name: {{this.name}}</p>
    <p>age: {{this.profile.age}}</p>
    {{if (this.sex) {}}
        <p>sex: {{this.sex}}</p>
    {{}}}
    <ul>
        {{for(var i in this.skills){}}
        <li>{{this.skills[i]}}</li>
        {{}}}
    </ul>
</script>

很显著我们这个模板就相对前面的庞杂得多了,然则基础思绪是一样的,无非是提取{{…}}的内容,然后连系数据从新组合成新的html字符串。然则与前面差别的是我们分开的子字符串中有三种范例:

  1. 含一般html标签的子字符串(如:<p>name:

  2. 含js对象值的子字符串(如:this.name

  3. 含javascript关键字的代码片断(如:if (this.sex) {

我们方才前面一向只提到了第1、2两种,这两种直接可以运用数组push要领就可以衔接起来,然则第3中不能运用数组push,而是应当直接衔接。
所以这里我们分两种状况:

var reExp = /(^( )?(var|if|for|else|switch|case|break|{|}|;))(.*)?/g,;

var code = 'var r=[];\n';
// 剖析html
function parsehtml(line) {
    // 单双引号转义,换行符替换为空格,去掉前后的空格
    line = line.replace(/('|")/g, '\\$1').replace(/\n/g, ' ').replace(/(^\s+)|(\s+$)/g,"");
    code +='r.push("' + line + '");\n';
}

// 剖析js代码        
function parsejs(line) {   
    // 去掉前后的空格
    line = line.replace(/(^\s+)|(\s+$)/g,"");
    code += line.match(reExp)? line + '\n' : 'r.push(' + line + ');\n';
}

当我们写完这两个函数的时刻,我们直接替换上面我们分开字符串时刻获得的字字符串时刻打印的函数即可。固然我们会看到许多文章为了紧缩代码,将这两个函数合并成一个函数,实在关于我们邃晓这个题目,复原题目实质并没有现实意义,这里发起照样很开写更清楚。

完全代码以下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <div id="content"></div>        
    <script type="text/tpl" id="template">
        <p>name: {{this.name}}</p>
        <p>age: {{this.profile.age}}</p>
        {{if (this.sex) {}}
            <p>sex: {{this.sex}}</p>
        {{}}}
        <ul>
            {{for(var i in this.skills){}}
            <li>{{this.skills[i]}}</li>
            {{}}}
        </ul>
    </script>
        
    <script type="text/javascript">    
        var mtpl = function(tpl, data) {
            var re = /{{(.+?)}}/g, 
                cursor = 0
                reExp = /(^( )?(var|if|for|else|switch|case|break|{|}|;))(.*)?/g,    
                code = 'var r=[];\n';

            // 剖析html
            function parsehtml(line) {
                // 单双引号转义,换行符替换为空格,去掉前后的空格
                line = line.replace(/('|")/g, '\\$1').replace(/\n/g, ' ').replace(/(^\s+)|(\s+$)/g,"");
                code +='r.push("' + line + '");\n';
            }
            
            // 剖析js代码        
            function parsejs(line) {   
                // 去掉前后的空格
                line = line.replace(/(^\s+)|(\s+$)/g,"");
                code += line.match(reExp)? line + '\n' : 'r.push(' + line + ');\n';
            }    
            
            while((match = re.exec(tpl))!== null) {
                // 最先标签  {{ 前的内容和完毕标签 }} 后的内容
                parsehtml(tpl.slice(cursor, match.index))
                // 最先标签  {{ 和 完毕标签 }} 之间的内容
                parsejs(match[1])
                // 每一次婚配完成挪动指针
                cursor = match.index + match[0].length;
            }
            // 末了一次婚配完的内容
            parsehtml(tpl.substr(cursor, tpl.length - cursor));
            code += 'return r.join("");';
            return new Function(code.replace(/[\r\t\n]/g, '')).apply(data);
        }
        
        var tpl = document.getElementById("template").innerHTML.toString();
        document.getElementById("content").innerHTML = mtpl(tpl,{
            name: "zhaomenghuan",
            profile: { 
                age: 22 
            },
            sex: 'man',
            skills: ['html5','javascript','android']
        });
    </script>
</body>
</html>

别的一个自定义标签的和这个代码相似,人人可以本身尝尝,或许看本文一切的代码演示完全工程。

至此我们完成了一个基于正则表达式的简朴js模板引擎,本文目标不在于造一个轮子,也不是为了反复制作文章,只是把相干题目举行梳理,在本身学问系统中构成一个越发清楚的思绪,经由过程这个现实例子将正则表达式、数组和字符串相干学问点串起来,方面背面本身查阅,也轻易初学者可以进修自创。

参考文章

MDN JavaScript 规范库 RegExp
js正则表达式基础语法(精炼)
只要20行Javascript代码!手把手教你写一个页面模板引擎
TemplateEngine.js源代码
template.js源代码

《JavaScript进阶进修(一)—— 基于正则表达式的简朴js模板引擎完成》

    原文作者:匠心
    原文地址: https://segmentfault.com/a/1190000005804719
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞