使用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
是会被忽略的,这也算是一次额外的收获。