摘要:基本語法構造函數(shù)可創(chuàng)建一個正則表達式對象,用特定的模式匹配文本。要表示字符串,字面量形式不使用引號,而傳遞給構造函數(shù)的參數(shù)使用引號。當使用構造函數(shù)創(chuàng)造正則對象時,需要常規(guī)的字符轉義規(guī)則在前面加反斜杠。結果替換與正則表達式匹配的子串。
前言文章來源:小青年原創(chuàng)
發(fā)布時間:2016-06-26
關鍵詞:JavaScript,正則表達式,js模板引擎
轉載需標注本文原始地址: http://zhaomenghuan.github.io...
這年頭MVC、MVVM框架泛濫,很多時候我們只是用了這些框架,有時候想深入去了解這些框架背后的原理實現(xiàn)時,閱讀源碼時發(fā)現(xiàn)無從下手,js基本功簡直渣渣,所以想利用業(yè)余時間還是要補補基礎。以前看JavaScript的一些書籍時總是把正則表達式這一章跳過了,遇到一些需要寫正則的時候然后都是各種copy,js要進階感覺還是要系統(tǒng)學習一下正則,雖然看起來像亂碼一樣的匹配規(guī)則,但是如果熟練使用會很有用,那么今天就先從正則開始吧!和大部分書籍一樣,本文前篇也會是講解基礎,本文的很多內(nèi)容都是摘自網(wǎng)絡進行整理,有些內(nèi)容需要各位自己進行實踐驗證。
正則表達式正則表達式(英語:Regular Expression,在代碼中常簡寫為regex、regexp或RE)使用單個字符串來描述、匹配一系列符合某個句法規(guī)則的字符串搜索模式。搜索模式可用于文本搜索和文本替換。
基本語法RegExp 構造函數(shù)可創(chuàng)建一個正則表達式對象,用特定的模式匹配文本。創(chuàng)建一個正則對象的兩種方法:字面量和構造函數(shù)。要表示字符串,字面量形式不使用引號,而傳遞給構造函數(shù)的參數(shù)使用引號。
字面量: /pattern/flags 構造函數(shù): RegExp(pattern [, flags])
pattern 正則表達式文本
flags 該參數(shù)可以是下面單個值或者幾個值的任意組合:
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)狀態(tài),當正則表達式保持為常量時使用字面量。例如當你在循環(huán)中使用字面量構造一個正則表達式時,正則表達式不會在每一次迭代中都被重新編譯(recompiled)。
而正則表達式對象的構造函數(shù),如 new RegExp("ab+c") 提供了正則表達式運行時編譯(runtime compilation)。如果你知道正則表達式模式將會改變,或者你事先不知道什么模式,而是從另一個來源獲取,如用戶輸入,這些情況都可以使用構造函數(shù)。
從ECMAScript 6開始,當?shù)谝粋€參數(shù)為正則表達式而第二個標志參數(shù)存在時,new RegExp(/ab+c/, "i")不再拋出TypeError (“當從其他正則表達式進行構造時不支持標志”)的異常,取而代之,將使用這些參數(shù)創(chuàng)建一個新的正則表達式。
當使用構造函數(shù)創(chuàng)造正則對象時,需要常規(guī)的字符轉義規(guī)則(在前面加反斜杠 )。比如,以下是等價的:
var re = new RegExp("w+"); var re = /w+/;正則表達式中的特殊字符
字符類別(Character Classes)
字符集合(Character Sets)
邊界(Boundaries)
分組(grouping)與反向引用(back references)
數(shù)量詞(Quantifiers)
^和$ —— 作用是分別指出一個字符串的開始和結束。
"^The":表示所有以"The"開始的字符串"There","The cat"等;
"of despair$":表示所以以"of despair"結尾的字符串;
"^abc$":表示開始和結尾都是"abc"的字符串——呵呵,只有"abc"自己了;
"notice":表示任何包含"notice"的字符串。
最后那個例子,如果你不使用兩個特殊字符,你就在表示要查找的串在被查找串的任意部分——你并不把它定位在某一個頂端。
*,+ , ? 和 {} —— 表示一個或一序列字符重復出現(xiàn)的次數(shù)。
分別表示“沒有或更多”,“一次或更多”,“沒有或一次”和指定重復次數(shù)的范圍。{}必須指定范圍的下限,如:"{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"后面跟著一個任意字符和一個數(shù)字;
"^.{3}$":表示有任意三個字符的字符串(長度為3個字符);
[] —— 表示某些字符允許在一個字符串中的某一特定位置出現(xiàn)
"[ab]":表示一個字符串有一個"a"或"b"(相當于"a|b");
"[a-d]":表示一個字符串包含小寫的"a"到"d"中的一個(相當于"a|b|c|d"或者"[abcd]");
"^[a-zA-Z]":表示一個以字母開頭的字符串;
"[0-9]%":表示一個百分號前有一位的數(shù)字;
",[a-zA-Z0-9]$":表示一個字符串以一個逗號后面跟著一個字母或數(shù)字結束。
可以在方括號里用"^"表示不希望出現(xiàn)的字符,"^"應在方括號里的第一位。(如:%[^a-zA-Z]%表示兩個百分號中不應該出現(xiàn)字母)。為了逐字表達,你必須在^.$()|*+?{這些字符前加上轉移字符""。在方括號中,不需要轉義字符。
RegExp對象的屬性屬性 | 含義 |
---|---|
$1-$9 | 如果它(們)存在,是匹配到的子串 |
$_ | 參見input |
$* | 參見multiline |
$& | 參見lastMatch |
$+ | 參見lastParen |
$` | 參見leftContext |
$"" | 參見rightContext |
constructor | 創(chuàng)建一個對象的一個特殊的函數(shù)原型 |
global | 是否在整個串中匹配(bool型) |
ignoreCase | 匹配時是否忽略大小寫(bool型) |
input | 被匹配的串 |
lastIndex | 最后一次匹配的索引 |
lastParen | 最后一個括號括起來的子串 |
leftContext | 最近一次匹配以左的子串 |
multiline | 是否進行多行匹配(bool型) |
prototype | 允許附加屬性給對象 |
rightContext | 最近一次匹配以右的子串 |
source | 正則表達式模式 |
lastIndex | 最后一次匹配的索引 |
詳情大家可以查看這里:MDN JavaScript 標準庫 RegExp屬性
RegExp對象的方法方法 | 含義 |
---|---|
compile | 正則表達式比較 |
exec | 執(zhí)行查找 |
test | 進行匹配 |
toSource | 返回特定對象的定義(literal representing),其值可用來創(chuàng)建一個新的對象。重載Object.toSource方法得到的。 |
toString | 返回特定對象的串。重載Object.toString方法得到的。 |
valueOf | 返回特定對象的原始值。重載Object.valueOf方法得到 |
詳情大家可以查看這里:MDN JavaScript 標準庫 RegExp方法
不過在這里我們還是需要說明一下的是我們用得比較多的方法主要分為兩類:
RegExp 對象方法RegExp.prototype.compile() ——編譯正則表達式
用法:regexObj.compile(pattern, flags)
功能說明:compile() 方法用于在腳本執(zhí)行過程中編譯正則表達式,也可用于改變和重新編譯正則表達式。該方法可以編譯指定的正則表達式,編譯之后的正則表達式執(zhí)行速度將會提高,如果正則表達式多次被調(diào)用,那么調(diào)用compile方法可以有效的提高代碼的執(zhí)行速度,如果該正則表達式只能被使用一次,則不會有明顯的效果。
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 方法返回一個數(shù)組,并且更新正則表達式對象的屬性。返回的數(shù)組包括匹配的字符串作為第一個元素,緊接著一個元素對應一個成功匹配被捕獲的字符串的捕獲括號(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 必需。規(guī)定要匹配的模式的 RegExp 對象。如果該參數(shù)不是 RegExp 對象,則需要首先把它傳遞給 RegExp 構造函數(shù),將其轉換為 RegExp 對象。返回值類型為Array。
功能說明:match() 方法可在字符串內(nèi)檢索指定的值,或找到一個或多個正則表達式的匹配。match() 方法將檢索字符串 String Object,以找到一個或多個與 regexp 匹配的文本。這個方法的行為在很大程度上有賴于 regexp 是否具有標志 g。如果 regexp 沒有標志 g,那么 match() 方法就只能在 stringObject 中執(zhí)行一次匹配。如果沒有找到任何匹配的文本, match() 將返回 null。否則,它將返回一個數(shù)組,其中存放了與它找到的匹配文本有關的信息。
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 必須。規(guī)定子字符串或要替換的模式的 RegExp 對象。
請注意,如果該值是一個字符串,則將它作為要檢索的直接量文本模式,而不是首先被轉換為 RegExp 對象。
newvalue 必需。一個字符串值。規(guī)定了替換文本或生成替換文本的函數(shù)。
返回值為String類型,一個新的字符串,是用 replacement 替換了 regexp 的第一次匹配或所有匹配之后得到的。
split 把字符串分割為字符串數(shù)組。
用法:string.split(separator,limit)
separator 可選。字符串或正則表達式,從該參數(shù)指定的地方分割 string Object。
limit 可選。該參數(shù)可指定返回的數(shù)組的最大長度。如果設置了該參數(shù),返回的子串不會多于這個參數(shù)指定的數(shù)組。如果沒有設置該參數(shù),整個字符串都會被分割,不考慮它的長度。
返回值為Array類型,一個字符串數(shù)組。該數(shù)組是通過在 separator 指定的邊界處將字符串 string Object 分割成子串創(chuàng)建的。返回的數(shù)組中的字串不包括 separator 自身。
var str="How are you doing today?"; var match = str.split(/ /); document.write(match); 結果: How,are,you,doing,today?正則表達式的應用實例說明
校驗是否全由數(shù)字組成 —— /^[0-9]{1,20}$/
^ 表示打頭的字符要匹配緊跟^后面的規(guī)則
$ 表示打頭的字符要匹配緊靠$前面的規(guī)則
[ ] 中的內(nèi)容是可選字符集
[0-9] 表示要求字符范圍在0-9之間
{1,20}表示數(shù)字字符串長度合法為1到20,即為[0-9]中的字符出現(xiàn)次數(shù)的范圍是1到20次。
/^ 和 $/成對使用應該是表示要求整個字符串完全匹配定義的規(guī)則,而不是只匹配字符串中的一個子串。
校驗登錄名:只能輸入5-20個以字母開頭、可帶數(shù)字、“_”、“.”的字串—— /^[a-zA-Z]{1}([a-zA-Z0-9]|[._]){4,19}$/
^[a-zA-Z]{1} 表示第一個字符要求是字母。
([a-zA-Z0-9]|[._]){4,19} 表示從第二位開始(因為它緊跟在上個表達式后面)的一個長度為4到9位的字符串,它要求是由大小寫字母、數(shù)字或者特殊字符集[._]組成。
校驗密碼:只能輸入6-20個字母、數(shù)字、下劃線——/^(w){6,20}$/
w:用于匹配字母,數(shù)字或下劃線字符
校驗普通電話、傳真號碼:可以“+”或數(shù)字開頭,可含有“-” 和 “ ”——/^[+]{0,1}(d){1,3}[ ]?([-]?((d)|[ ]){1,12})+$/
d:用于匹配從0到9的數(shù)字;
“?”元字符規(guī)定其前導對象必須在目標對象中連續(xù)出現(xiàn)零次或一次
可以匹配的字符串如:+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模板引擎實現(xiàn)原理
前面我們花了很長的篇幅講解正則表達式是為了大家看這部分是時候能夠有所理解,如果前面的內(nèi)容一下子沒有看懂也沒有關系,大家可以先看看這部分的內(nèi)容回過頭去查看剛剛的內(nèi)容。
我們首先會想到寫一個模板,我們常見的是寫成這樣:
當然也可以使用標簽,而且這個也是現(xiàn)在的流行趨勢,擁抱模塊化,不過本文不是講這個標簽和模塊化,如果大家感興趣可以看看這兩篇文章:
HTML5 標簽元素簡介
Web Components 是什么?它為什么對我們這么重要?
大家也可以看下面這個基礎例子:
name: 小青年
age: 22
回歸正題,我們繼續(xù)說上面的模板,開始寫模板引擎。
基礎模板引擎原理講解我們使用js模板引擎,可以認為是在做一個MVC結構的系統(tǒng),模型(model)-視圖(view)-控制器(controller)。控制器(controller)作為中間部分,首先要拿到模型,這里我們需要拿到模板里面與視圖相關的內(nèi)容,如上面的例子中{{ }}中的內(nèi)容,首先用正則查找:
var re = /{{(.+?)}}/g; while((match = re.exec(tpl))!==null) { console.log(match); } 結果: "{{name}},name" "{{age}},age"
/{{(.+?)}}/g的意思是查找開頭為{{和結尾為}}的子字符串。通過RegExp 對象exec()方法搜索匹配得到的是一個數(shù)組,我們可以通過match[0]表示匹配的原字符串,match[1]表示匹配的目標字符串,我們通過執(zhí)行字符串替換方法就可以得到目標字符串。
這里我們通過data["key"]的形式取值然后替換模板中的{{...}}的內(nèi)容實現(xiàn)了一個內(nèi)容的替換。上述代碼很簡單,基本實現(xiàn)了一個字符替換而已,我們上面是通過字符串替換實現(xiàn)了模板和數(shù)據(jù)的匹配,但是如果我們上面那個json數(shù)據(jù)是這樣的:
var data = { base: { name: "zhaomenghuan", age: 22 }, skills: ["html5","javascript","android"] }
我們直接通過data[match[1]]進行顯然會有問題,我們雖然可以通過data.base["name"]獲取,但是對于模板引擎函數(shù)封裝來說是不夠完善的,而且也不能執(zhí)行JavaScript,好像并沒有類似于一些有名的js模板引擎庫中的語法功能,所以略顯low。下面我們在這個基礎上進行改造。
下面我們說一下一種最原始的方法,通過Function構造器實現(xiàn),根據(jù)字符串創(chuàng)建一個函數(shù)。在一篇文章中看到說這種方法執(zhí)行JavaScript性能低下,但是對于初學者來說,學習一下實現(xiàn)思路我覺得也是有意義的,畢竟對于新手來說談性能是件奢侈的事。我們首先看個例子:
var fn = new Function("arg", "console.log(arg + 1);"); fn(2); // outputs 3
fn可是一個貨真價實的函數(shù),它接受一個參數(shù),函數(shù)體是console.log(arg + 1);,上面那個例子等同于:
var fn = function(arg) { console.log(arg + 1); } fn(2);
我們通過new Function可以將字符串轉成JavaScript執(zhí)行,看起來是不是很すごい(厲害,"sigoyi",好像還有個單詞是‘daijobu’,女朋友每次問我,我每次都是回答‘daijo’,女朋友喜歡看動漫,今天她放假先回家了,舍不得,想想她平時萌萌噠的樣子,越是想念,學幾個簡單單詞,下次見面說說,哈哈。)
接著說,我們有時候參數(shù)是多個,我們雖然可以輸入多個參數(shù),但是這顯然不是最好的辦法,我們可以使用apply,這樣我們不必顯式地傳參數(shù)給這個函數(shù)。比如我們前面的例子:
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()會自動設定函數(shù)執(zhí)行的上下文,這就是為什么我們能在函數(shù)里面使用this.name,這里this指向data對象。這里我們得到什么啟示呢?我們考驗通過給new Function傳入模板字符串和數(shù)據(jù)生成我們的內(nèi)容。
我們可以通過數(shù)組push()或者+=拼接方式將分隔的字符串連接起來,有文章中稱,“在現(xiàn)代瀏覽器使用+=會比數(shù)組push方法快,在v8引擎中使用+=方法會比數(shù)組拼接快4.7倍,而在IE6-8下push比+=拼接快”。至于二者效率比較不在本文范圍內(nèi),大家可以自行探究,但是我們?yōu)榱撕喕瘑栴},不考慮效率問題,我們可以將分隔的字符串用下列方法push拼接:
var r=[]; r.push(""); r.push(this.name); r.push("
"); r.push(this.age); r.push("
"); return r.join("");
我們?nèi)绻苯悠唇映蓴?shù)組然后轉成對象也可以,但是需要將<>轉義,為了方便,我們有時候可以這樣處理:
var data = { name: "zhaomenghuan", age: 22 } var code = "var r=[]; "; code += "r.push(""); "; code += "r.push(this.name); " code += "r.push("
"); "; code += "r.push(this.age); "; code += "r.push("
"); "; code += "return r.join("");"; console.log(code) var fn = new Function(code.replace(/[ ]/g, "")).apply(data); console.log(fn) 結果: "zhaomenghuan
22
"
寫到這里我相信聰明的人應該知道我接下來要做的事情了,主要是兩個:如何根據(jù)我們自定義的分隔字符分隔模板字符串,然后就是動態(tài)生成字符串。不過我們可以看出來這里我們還有個沒有提到的是讓模板能夠嵌入JavaScript的語法關鍵詞,比如if,for等,這個處理方法和上面的略有不同,需要加以判斷,不過我們可以劃分為解析html和js兩大類。
自定義分隔字符分隔模板字符在講如何分割字符串前我們先看三個函數(shù):slice|splice|split。
哈哈,是不是懵逼了?反正我經(jīng)常是暈的,不行,還是要對比一下搞清楚。
常用函數(shù) slice | splice | split 對比String.prototype.slice() —— 從一個字符串中提取字符串并返回新字符串
語法:str.slice(beginSlice[, endSlice])
參數(shù):
beginSlice:從該索引(以 0 為基數(shù))處開始提取原字符串中的字符。如果值為負數(shù),會被當做 sourceLength + beginSlice 看待,這里的sourceLength 是字符串的長度 (例如, 如果beginSlice 是 -3 則看作是: sourceLength - 3)
endSlice:可選。在該索引(以 0 為基數(shù))處結束提取字符串。如果省略該參數(shù),slice會一直提取到字符串末尾。如果該參數(shù)為負數(shù),則被看作是 sourceLength + endSlice,這里的 sourceLength 就是字符串的長度(例如,如果 endSlice 是 -3,則是, sourceLength - 3)。
Array.prototype.slice()—— 把數(shù)組中一部分的淺復制(shallow copy)存入一個新的數(shù)組對象中,并返回這個新的數(shù)組。
語法:arr.slice([begin[, end]])
參數(shù):
begin:從該索引處開始提取原數(shù)組中的元素(從0開始)。
如果該參數(shù)為負數(shù),則表示從原數(shù)組中的倒數(shù)第幾個元素開始提取,slice(-2)表示提取原數(shù)組中的倒數(shù)第二個元素到最后一個元素(包含最后一個元素)。如果省略 begin,則 slice 從索引 0 開始。
end:在該索引處結束提取原數(shù)組元素(從0開始)。slice會提取原數(shù)組中索引從 begin 到 end 的所有元素(包含begin,但不包含end)。
slice(1,4) 提取原數(shù)組中的第二個元素開始直到第四個元素的所有元素 (索引為 1, 2, 3的元素)。如果該參數(shù)為負數(shù), 則它表示在原數(shù)組中的倒數(shù)第幾個元素結束抽取。 slice(-2,-1)表示抽取了原數(shù)組中的倒數(shù)第二個元素到最后一個元素(不包含最后一個元素,也就是只有倒數(shù)第二個元素)。如果 end 被省略,則slice 會一直提取到原數(shù)組末尾。
描述:
slice 不修改原數(shù)組,只會返回一個包含了原數(shù)組中提取的部分元素的一個新數(shù)組。原數(shù)組的元素會按照下述規(guī)則拷貝("一級深拷貝"[one level deep]規(guī)則):
如果該元素是個對象引用 (不是實際的對象),slice 會拷貝這個對象引用到新的數(shù)組里。兩個對象引用都引用了同一個對象。如果被引用的對象發(fā)生改變,則改變將反應到新的和原來的數(shù)組中。
對于字符串和數(shù)字來說(不是 String 和 Number 對象),slice 會拷貝字符串和數(shù)字到新的數(shù)組里。在一個數(shù)組里修改這些字符串或數(shù)字,不會影響另一個數(shù)組。
如果向兩個數(shù)組任一中添加了新元素,則另一個不會受到影響。
Array.prototype.splice() —— 用新元素替換舊元素,以此修改數(shù)組的內(nèi)容
語法:array.splice(start, deleteCount[, item1[, item2[, ...]]])
參數(shù):
start?
從數(shù)組的哪一位開始修改內(nèi)容。如果超出了數(shù)組的長度,則從數(shù)組末尾開始添加內(nèi)容;如果是負值,則表示從數(shù)組末位開始的第幾位。
deleteCount
整數(shù),表示要移除的數(shù)組元素的個數(shù)。如果 deleteCount 是 0,則不移除元素。這種情況下,至少應添加一個新元素。如果 deleteCount 大于start 之后的元素的總數(shù),則從 start 后面的元素都將被刪除(含第 start 位)。
itemN
要添加進數(shù)組的元素。如果不指定,則 splice() 只刪除數(shù)組元素。
返回值:
由被刪除的元素組成的一個數(shù)組。如果只刪除了一個元素,則返回只包含一個元素的數(shù)組。如果沒有刪除元素,則返回空數(shù)組。
描述:
如果添加進數(shù)組的元素個數(shù)不等于被刪除的元素個數(shù),數(shù)組的長度會發(fā)生相應的改變。請注意,splice() 方法與 slice() 方法的作用是不同的,splice() 方法會直接對數(shù)組進行修改。
String.prototype.split() —— 通過把字符串分割成子字符串來把一個 String 對象分割成一個字符串數(shù)組
語法:string.split(separator,limit)
我們在前面講解【支持正則表達式的 String 對象的方法】時講到這個方法了,這里不再贅述,列出來只為方便大家對比學習。
這里列出的方法中對于我們分隔字符串有用的是String.prototype.slice()和String.prototype.split(),另個方法的區(qū)別在于使用slice()方法們需要知道子字符串的索引值index,使用split()方法我們需要子字符串的內(nèi)容或者符合的正則表達式。很明顯我們的思路就出來了,接下來我們用代碼實現(xiàn)。
基于String.prototype.slice()方法分隔字符串我們這里參考前面的內(nèi)容寫一個基本函數(shù),設置一個變量cursor作為索引值指針,每次執(zhí)行完一個匹配操作,我們將指針移動一下。我們前面講RegExp.prototype.exec()返回值時重點說到了三個參數(shù)。
若match = re.exec(str),則有:
match.index:匹配的對象起始位置
match[0]:表示匹配的原字符串
match[1]:表示匹配的目標字符串
若我們明白了思路我們就可以寫下面的代碼:
通過上面的代碼我們已經(jīng)實現(xiàn)了將模板字符串分隔成子字符串。
基于String.prototype.split()方法分隔字符串使用字符串split()方法,下面我們不使用正則作為分隔符號,這里就使用自定義符號作為分隔標準,如:
var sTag = "{%";//開始標簽 var eTag = "%}";//結束標簽
然后我們以sTag為分隔符執(zhí)行split方法:
var matchs = tpl.split(sTag);
返回值matchs 為一個子字符串數(shù)組,然后對數(shù)組每一個子字符串以eTag為分隔符執(zhí)行split方法,進一步得到的子字符串數(shù)組分為兩種類型,一種是于我們匹配子字符串參數(shù)有關的子串數(shù)組,一種是與匹配參數(shù)無關的子串數(shù)組(這種數(shù)組長度為1)。之所以要分得這么細是為了后面字符串連接時更方便的時候合適的方法連接。
動態(tài)連接字符串
我們上面使用了字符串分隔函數(shù)把字符串進行了分隔,并且提取了關鍵子字符串,下面我們講解如何將這些字符串連接起來。
我們這里定義一個這樣的模板:
很明顯我們這個模板就相對前面的復雜得多了,但是基本思路是一樣的,無非是提取{{...}}的內(nèi)容,然后結合數(shù)據(jù)重新組合成新的html字符串。但是與前面不同的是我們分隔的子字符串中有三種類型:
含普通html標簽的子字符串(如:
name:)
含js對象值的子字符串(如:this.name)
含javascript關鍵字的代碼片段(如:if (this.sex) {)
我們剛剛前面一直只提到了第1、2兩種,這兩種直接可以使用數(shù)組push方法就可以連接起來,但是第3中不能使用數(shù)組push,而是應該直接連接。
所以這里我們分兩種情況:
var reExp = /(^( )?(var|if|for|else|switch|case|break|{|}|;))(.*)?/g,; var code = "var r=[]; "; // 解析html function parsehtml(line) { // 單雙引號轉義,換行符替換為空格,去掉前后的空格 line = line.replace(/("|")/g, "$1").replace(/ /g, " ").replace(/(^s+)|(s+$)/g,""); code +="r.push("" + line + ""); "; } // 解析js代碼 function parsejs(line) { // 去掉前后的空格 line = line.replace(/(^s+)|(s+$)/g,""); code += line.match(reExp)? line + " " : "r.push(" + line + "); "; }
當我們寫完這兩個函數(shù)的時候,我們直接替換上面我們分隔字符串時候得到的字字符串時候打印的函數(shù)即可。當然我們會看到很多文章為了壓縮代碼,將這兩個函數(shù)合并成一個函數(shù),其實對于我們理解這個問題,還原問題本質(zhì)并沒有實際意義,這里建議還是很開寫更清晰。
完整代碼如下:
另外一個自定義標簽的和這個代碼類似,大家可以自己試試,或者看本文所有的代碼演示完整工程。
至此我們完成了一個基于正則表達式的簡單js模板引擎,本文目的不在于造一個輪子,也不是為了重復制造文章,只是把相關問題進行梳理,在自己知識體系中形成一個更加清晰的思路,通過這個實際例子將正則表達式、數(shù)組和字符串相關知識點串起來,方面后面自己查閱,也方便初學者可以學習借鑒。
參考文章MDN JavaScript 標準庫 RegExp
js正則表達式基本語法(精粹)
只有20行Javascript代碼!手把手教你寫一個頁面模板引擎
TemplateEngine.js源代碼
template.js源代碼
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79771.html
摘要:當我們的視圖和數(shù)據(jù)任何一方發(fā)生變化的時候,我們希望能夠通知對方也更新,這就是所謂的數(shù)據(jù)雙向綁定。返回值返回傳入函數(shù)的對象,即第一個參數(shù)該方法重點是描述,對象里目前存在的屬性描述符有兩種主要形式數(shù)據(jù)描述符和存取描述符。 前言 談起當前前端最熱門的 js 框架,必少不了 Vue、React、Angular,對于大多數(shù)人來說,我們更多的是在使用框架,對于框架解決痛點背后使用的基本原理往往關注...
摘要:第三部分介紹一種模板引擎,之所以介紹他是因為。。。。三簡介模板引擎是數(shù)據(jù)與界面分離工作中最重要一環(huán)。是新一代模板引擎,它采用預編譯方式讓性能有了質(zhì)的飛躍,并且充分利用引擎特性,使得其性能無論在前端還是后端都有極其出色的表現(xiàn)。 嗯,這也是一個學習筆記,材料均來自網(wǎng)絡,有改動。。文章主要分為三部分,第一部分通過實現(xiàn)一個簡易的ERB模板引擎來介紹其原理,參考javascript模板引擎和實現(xiàn)...
摘要:個人前端文章整理從最開始萌生寫文章的想法,到著手開始寫,再到現(xiàn)在已經(jīng)一年的時間了,由于工作比較忙,更新緩慢,后面還是會繼更新,現(xiàn)將已經(jīng)寫好的文章整理一個目錄,方便更多的小伙伴去學習。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 個人前端文章整理 從最開始萌生寫文章的想法,到著手...
閱讀 2571·2021-11-24 09:38
閱讀 2601·2019-08-30 15:54
閱讀 915·2019-08-30 15:52
閱讀 1909·2019-08-30 15:44
閱讀 2712·2019-08-30 13:48
閱讀 768·2019-08-29 16:21
閱讀 996·2019-08-29 14:03
閱讀 2212·2019-08-28 18:15