正则灵异事件?校验结果跟预期不一致,标准手机号校验不通过!

发布时间 2023-05-31 11:07:59作者: 你比从前快乐KX

今天一位同事跟我说遇到个奇怪的问题。自己写了个简单的手机号校验正则表达式,然后明明输入的标准手机号却一直走的校验不通过分支。我同事已经开始怀疑自己的手机号是假的了?,大伙先看下代码示例:

const RegExDemo = () => {
    const [tel, setTel] = useState(() => '');
    
    const $msg = useRef(null);

     //  使用useMemo()缓存正则表达式,避免每次都要计算
     const  validateTel  =  useMemo(()  =>  {
        //  设置除了"1"以外的第一个字符必须是3-9之间的 其余10位是任意数字
        return  /^1[3-9]\d{9}$/g;
    },  []);

     //  使用useEffect()处理DOM更新
    useEffect(() => {
        if (!$msg.current) return;

        console.log('validateTel.test(tel):', validateTel.test(tel));
        if(validateTel.test(tel)) {
           $msg.current.style.display = 'none';
        } else {
            $msg.current.style.display = 'block';
        }
    }, [tel, validateTel]);


    /**
     * 输入框值变化
     * @param {} event 
     */
    const _onInputChange = (event) => {
        //  更新组件状态,将tel存储在useState回调中
        setTel(()  =>  {
            return  event.target.value;
        });
    }
    return (
        <div className='form'>
            <span>手机号</span>
            <Input type='text' className='tel-input' value={tel} onChange={_onInputChange}/>
            <span ref={$msg} className='msg'>手机号格式不正确</span>
        </div>
    );
};

看上去好像没什么问题吧,然后在输入框里输入标准手机号,看下结果:

多么标准的手机号,而且明明打印的校验结果是true,竟然提示格式不正确?离了个大谱!要知道这个问题原因,就要看正则的掌握情况如何。

问题原因
所有的正则表达式都有一个lastIndex属性,用于标识下一次匹配开始查找的位置。如果未使用全局匹配,lastIndex的值始终为0,而如果通过使用‘g’标识符或者设置global属性值为true时,那么正则表达式将对要匹配的字符串进行全局匹配,会执行多次匹配。在使用全局匹配的情况下,找到匹配的项后lastIndex的值被设置为匹配内容的下一个字符在字符串中的位置索引,找不到匹配项则设置为0;
在上面的例子中加了全局标识‘g’,然后在判断语句前的打印语句中调用了test方法,在匹配成功后lastIndex的值已经设置成了11,所以在if语句中的那次test就是从索引为11的位置去匹配,结果就成了false。将lastIndex的值打印出来看下,如图:

解决办法

1.删除日志打印行(这种方式发生bug的概率还是比较高),如下:

2.去掉全局标识‘g’

3.如果非得使用全局的情况,那么可以将lastIndex重置为0