使用JavaScript的split
方法拆分字符串時出現一些空字符串""
,尤爲是當使用正則表達式做爲分隔符的時候。javascript
在上面這個問題中,題主使用正則表達式對字符串進行分割時產生了多個空字符串""
,代碼以下:正則表達式
'張sdf四上法asdf翁芬aa33網s'.split(/([\u4e00-\u9fa5]{1})/gi); //輸出["", "張", "sdf", "四", "", "上", "", "法", "asdf", "翁", "", "芬", "aa33", "網", "s"]
那麼,產生這些空字符串的緣由是什麼?segmentfault
在Google上搜索了一番,發現相關的結果並很少,即使有,詳細解釋的也很少,大概的說了一下,而後就給出了一個ECMAScript規範的連接。看來要想知道真正的緣由,就只能硬着頭皮看規範了。數組
那麼,接下來,按照國際慣例,先上ECMAScript的標準鎮樓。google
這個章節詳細介紹了split
方法的執行步驟,若是感興趣的話能夠一步一步的認真看完,我在這裏只把和產生空字符串相關的步驟拿出來解釋一下,不當之處,歡迎你們提出。prototype
摘取部分步驟:code
整個過程當中最主要的步驟是第13步這個循環,而這個循環主要作的事情以下:javascript正則
p
, q
的值,每一次循環開始的時候p
和q
的值是相同的(該步驟在循環以外);SplitMatch(S, q, R)
這個方法對字符串進行拆分;ⅲ
;ⅲ
又分紅了8個小步用來將返回的結果填充到事先定義好的數組A
中1
的做用是返回原始字符串的一個子串,開始位置是p
(包含在內),結束位置是q
(不包含在內),注意:在這一步中會產生空字符串,我將其標記爲截取字符串
,方便下文引用。A
中A
中,和產生空字符串無關)SplitMatch(S, q, R)
接下來,咱們須要瞭解一下SplitMatch(S, q, R)
這個方法作了些什麼事。這個方法在split
規範中的下方有說起。它主要作的事是,根據分隔符(separator
)的類型進行相應的操做:
RegExp
類型的,調用RegExp
的內部方法[[Match]]
來對字符串進行匹配,若是匹配失敗,返回failure
,不然,返回一個MatchResult
類型的結果。failure
,成功返回MatchResult
類型的結果。MatchResult
上面的步驟中又引出了一個MatchResult
類型的變量。經過查文檔發現,該類型的變量有兩個屬性endIndex
和captures
,endIndex
的值是字符串匹配的位置加上1,captures
能夠理解爲一個數組,當分隔符爲正則表達式時,它裏面的元素是分組捕獲的值;當分隔符爲字符串時,它爲一個空數組。
咱們從上面的步驟能夠看出,分割的字符串是在截取字符串
這一步驟中產生的(正則表達式的分組捕獲除外)。它的做用是截取指定開始(包含在內)和結束位置(不包含在內)之間的字符串,那它何時會返回""
呢?有一種特殊狀況是開始位置和結束位置的值相等,這只是猜測而已,由於該規範沒有給出截取字符串的規範步驟。
都走到這裏了,爲何再也不往前走一步呢?
因而,我試着搜索了一些V8的源碼,看看能不能找到具體的實現方法。確實找到了相關的代碼,源碼連接
這裏摘取其中一部分:
function StringSplitJS(separator, limit) { ... ... //分隔符是字符串的狀況 if (!IS_REGEXP(separator)) { var separator_string = TO_STRING_INLINE(separator); if (limit === 0) return []; // ECMA-262 says that if separator is undefined, the result should // be an array of size 1 containing the entire string. if (IS_UNDEFINED(separator)) return [subject]; var separator_length = separator_string.length; //分隔符是空字符串,直接返回了字符數組 if (separator_length === 0) return %StringToArray(subject, limit); var result = %StringSplit(subject, separator_string, limit); return result; } if (limit === 0) return []; // 分隔符是正則表達式的狀況,調用StringSplitOnRegExp return StringSplitOnRegExp(subject, separator, limit, length); } //此處省略若干代碼
我在代碼中發現,在填充數組的時候會調用%_SubString
這個方法來截取字符串,惋惜的是我沒有找到他的相關定義,若是有找到的同窗歡迎告知。可是,我發現JavaScript中substring
這個方法所對應的StringSubstring
這個方法會調用%_SubString
這個方法,並將其結果返回。那麼若是'abc'.substring(1,1)
返回""
,則代表%_SubString
這個方法在開始位置和結束位置相同的時候會返回""
,結果你們一試便知。
那麼,何時會出現開始位置等於結束位置(即q === p
)的狀況呢?我按照上面的步驟一步一步的進行分析,最終發現:
S
匹配過一次分隔符以後,緊接着,字符串S
的下一個位置還匹配分隔符。如:'abbbc'.split('b')
,'abbbc'.split(/(b){1}/)
'abc'.split('a')
,'abc'.split(/ab/)
'abc'.split('c')
,'abc'.split(/bc/)
此外,當使用正則表達式做爲分隔符的時候,返回的結果中還有可能出現undefined
。
如:'abc'.split(/(d)*/)
回過頭來再看看開頭的那個例子,是否是知足上面幾種狀況?
這是我第一次這麼仔細的看ECMAScript的標準規範,看的過程確實很痛苦,但明白以後就感受很痛快了。也感謝題主提出的這個問題,以及追問。
順便提一句,正則表達式做爲分隔符時,global
修飾符g
是會被忽略的,這也算是一次額外的收穫。