资讯专栏INFORMATION COLUMN

JavaScript进阶学习(一)—— 基于正则表达式的简单js模板引擎实现

Magicer / 1174人阅读

摘要:基本语法构造函数可创建一个正则表达式对象,用特定的模式匹配文本。要表示字符串,字面量形式不使用引号,而传递给构造函数的参数使用引号。当使用构造函数创造正则对象时,需要常规的字符转义规则在前面加反斜杠。结果替换与正则表达式匹配的子串。

文章来源:小青年原创
发布时间: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+/;
正则表达式中的特殊字符

字符类别(Character Classes)

字符集合(Character Sets)

边界(Boundaries)

分组(grouping)与反向引用(back references)

数量词(Quantifiers)

^$ —— 作用是分别指出一个字符串的开始和结束。

"^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+"
"); 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 + "
"); } 结果: 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}$/

^ 表示打头的字符要匹配紧跟^后面的规则
$ 表示打头的字符要匹配紧靠$前面的规则
[ ] 中的内容是可选字符集
[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}://.+$/ 或 /^http[s]{0,1}://.{1,n}$/ (表示url串的长度为length(“https://”) + n )

/ :表示字符“/”。
. 表示所有字符的集
+ 等同于{1,},就是1到正无穷吧。

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

[u4E00-u9FA5] :中文字符集的范围

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



    
    


    
规则表达式 : (填写/ /之间的表达式)

校验字符串 :
js模板引擎实现原理

前面我们花了很长的篇幅讲解正则表达式是为了大家看这部分是时候能够有所理解,如果前面的内容一下子没有看懂也没有关系,大家可以先看看这部分的内容回过头去查看刚刚的内容。

我们首先会想到写一个模板,我们常见的是写成这样:

当然也可以使用标签,而且这个也是现在的流行趋势,拥抱模块化,不过本文不是讲这个标签和模块化,如果大家感兴趣可以看看这两篇文章:

HTML5