00 在 JS 正则表现【源码分析】
imeay opened this issue · 0 comments
imeay commented
Preview
帮女朋友想一个匹配数字的输入的表达式, 要求整数或者小数,小数点后有且只能2位。 一会功夫,正则就出来了
/^(0|[1-9]\d*)(\.\d{1,2})?$/
验证过程中, 发现有一个奇怪的现象,数字 00 竟然也可以通过
> reg.test(00)
true
> reg.test('00')
false
这个就让我很好奇了,为什么 数字 00 在这里竟然能通过正则表达式。 不禁想看下 RegExp.prototype.test 实现,在网上找下一下源码
https://chromium.googlesource.com/v8/v8/+/4.3.49/src/regexp.js?autodive=0%2F%2F
关键代码有2段
第一段是方法名映射
InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, GlobalArray(
--
| "exec", RegExpExecJS,
| "test", RegExpTest,
| "toString", RegExpToString,
| "compile", RegExpCompileJS
| ));
第二段则是疑问的关键啦
function RegExpTest(string) {
--
| if (!IS_REGEXP(this)) {
| throw MakeTypeError('incompatible_method_receiver',
| ['RegExp.prototype.test', this]);
| }
| string = TO_STRING_INLINE(string);
|
| var lastIndex = this.lastIndex;
|
| // Conversion is required by the ES5 specification (RegExp.prototype.exec
| // algorithm, step 5) even if the value is discarded for non-global RegExps.
| var i = TO_INTEGER(lastIndex);
|
| if (this.global \|\| (harmony_regexps && this.sticky)) {
| if (i < 0 \|\| i > string.length) {
| this.lastIndex = 0;
| return false;
| }
| // matchIndices is either null or the $regexpLastMatchInfo array.
| var matchIndices = %_RegExpExec(this, string, i, $regexpLastMatchInfo);
| if (IS_NULL(matchIndices)) {
| this.lastIndex = 0;
| return false;
| }
| $regexpLastMatchInfoOverride = null;
| this.lastIndex = $regexpLastMatchInfo[CAPTURE1];
| return true;
| } else {
| // Non-global, non-sticky regexp.
| // Remove irrelevant preceeding '.*' in a test regexp. The expression
| // checks whether this.source starts with '.*' and that the third char is
| // not a '?'. But see https://code.google.com/p/v8/issues/detail?id=3560
| var regexp = this;
| if (regexp.source.length >= 3 &&
| %_StringCharCodeAt(regexp.source, 0) == 46 && // '.'
| %_StringCharCodeAt(regexp.source, 1) == 42 && // '*'
| %_StringCharCodeAt(regexp.source, 2) != 63) { // '?'
| regexp = TrimRegExp(regexp);
| }
| // matchIndices is either null or the $regexpLastMatchInfo array.
| var matchIndices = %_RegExpExec(regexp, string, 0, $regexpLastMatchInfo);
| if (IS_NULL(matchIndices)) {
| this.lastIndex = 0;
| return false;
| }
| $regexpLastMatchInfoOverride = null;
| return true;
| }
| }
其中有一行代码 string = TO_STRING_INLINE(string);
引起了我的注意,这里将输入的参数在内部转成字符串, 在浏览器试了一下00转字符串
String(00)
"0"
结果输出为"0", 这也就是解释了上面验证通过的原因了
00 -> '"0" , 而字符串 0 刚好满足正则规则