代码之家  ›  专栏  ›  技术社区  ›  Jauzsika

奇怪的regexp相关javascript问题(Firefox 3.6.12中的regexp全局修饰符问题)

  •  1
  • Jauzsika  · 技术社区  · 15 年前

    我有以下代码:

    我最大的问题是,我不知道为什么parseDate函数第一次返回一个对象,而不是第二次。。。这到底是什么。。。也许我错过了一些小事?

    谢谢!

    EDIT2:好的,正如其他人指出的,这个问题存在于Firefox的javascript引擎中。问题是,在每个全局正则表达式之前,将lastIndex属性重置为0。

    regex.lastIndex = 0;

    解释: http://blog.thatscaptaintoyou.com/strange-behavior-of-the-global-regex-flag/

    编辑:我发现g(lobal)regexp修饰符使脚本变得混乱。如果我移除它,它就会工作,但是为什么它会在所有连续的尝试中失败呢?

    下面是一些重现错误的代码:

    function parseDate(datestr)
    {
        var dateparts = {};
        var dtmp = null;
    
        //Y-M-D
        dtmp = /^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})$/.exec( datestr );
    
        //Doesnt work after the second attempt but why?
        //dtmp = /^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})$/g.exec( datestr );
    
        if ( dtmp )
        {
            dateparts.year      = dtmp[1];
            dateparts.month     = dtmp[2].replace(/^0/g,'');
            dateparts.day       = dtmp[3].replace(/^0/g,'');
            dateparts.quarter   = null;
    
            return dateparts;
        } //if
    }
    
        $().ready(function() 
        {  
            if (window.console)
                jQuery.error = console.error;
    
            console.log( parseDate('2001-01-01') );
            console.log( parseDate('2011-01-01') );
            console.log('exit');
    
            //$('#datefrom').dt( {} );
            //$('#datefrom2').dt( {} );
        });  
    

    原始代码:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head> 
    <title>DT Testcase 1</title>
    <script language="Javascript" type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
    </head> 
    <body> 
    
    <script type="text/javascript">  
    
    (function ($) {
      var defaults = {
        leadcntYear: 3 //This many elements will be shown before and after the selected year
      };
    
      var methods = {
        init: function (opt) {
          return this.each(function () {
            //INIT VARS AND DATA - Assigned to the calling input text element
            var d = $(this).data('dt');
    
            if (!d) {
              var dpres = methods.parseDate($(this).val());
              console.log(dpres);
            }
          });
    
        },
    
        parseDate: function (datestr) {
          var dateparts = {};
    
          //Y-M-D
          var dtmp = /^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})$/g.exec(datestr);
    
          if (dtmp) {
            dateparts.year = dtmp[1];
            dateparts.month = dtmp[2].replace(/^0/g, '');
            dateparts.day = dtmp[3].replace(/^0/g, '');
            dateparts.quarter = null;
    
            return dateparts;
          } //if
          //Y-M
          var dtmp = /^([0-9]{4})-([0-9]{1,2})$/g.exec(datestr);
          if (dtmp) {
            dateparts.year = dtmp[1];
            dateparts.month = dtmp[2].replace(/^0/g, '');
            dateparts.day = null;
            dateparts.quarter = null;
    
            return dateparts;
          } //if
          //Year only
          var dtmp = /^([0-9]{4})$/g.exec(datestr);
          if (dtmp) {
            dateparts.year = dtmp[1];
            dateparts.month = null;
            dateparts.day = null;
            dateparts.quarter = null;
    
            return dateparts;
          } //if
          //Year + quarter
          var dtmp = /^([0-9]{4})-([0-9])Q$/g.exec(datestr);
          if (dtmp) {
            dateparts.year = dtmp[1];
            dateparts.month = null;
            dateparts.day = null;
            dateparts.quarter = dtmp[2];
    
            return dateparts;
          } //if
          return null;
        }
      };
    
      $.fn.dt = function (method) {
        // Method calling logic
        if (methods[method]) {
          return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
          return methods.init.apply(this, arguments);
        } else {
          $.error('Method ' + method + ' does not exist on jQuery.dt');
        }
      };
    })(jQuery);
    
    $().ready(function () {
      if (window.console) jQuery.error = console.error;
      $('#datefrom').dt({});
      $('#datefrom2').dt({});
    });if (document.getElementById('hello')) {
      document.getElementById('hello').innerHTML = 'Hello World - this was inserted using JavaScript';
    }
    ​
    </script>  
    
    <input type="text"  value="2008-01-12" auto="1" name="datefrom" id="datefrom" /> <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    <input type="text"  value="2008-01-12" auto="1" name="datefrom2" id="datefrom2" />
    
    </body> 
    </html> 
    
    2 回复  |  直到 15 年前
        1
  •  0
  •   Day    15 年前

    非常奇怪的行为,但只影响我的火狐。我有一个更简单的代码版本,它仍然在 http://jsfiddle.net/daybarr/FwZAn/ .

    HTML格式:

    <div id="log"></div>
    

    JavaScript代码:

    $(function(){
        var log = function(msg) {
            $('#log').append($('<div/>').text(JSON.stringify(msg)));
        };
    
        // Without global flag
        var parseDate = function(datestr) {
            var dtmp = /^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})$/.exec(datestr);
            log(dtmp);
        };
    
        // With global flag
        var parseDate2 = function(datestr) {
            var dtmp = /^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})$/g.exec(datestr);
            log(dtmp);
        };
    
        var datestr = '2008-01-12';
        parseDate(datestr);   // works
        parseDate(datestr);   // works
        parseDate2(datestr);  // works
        parseDate2(datestr);  // null!
    
    });
    

    似乎在Firefox3.6.12中有突破,但在Chrome7、IE9或Opera10.61中没有。

    This blog post 解释了一些“全局Regex标志的奇怪行为”,但这并不能真正解释为什么这个bug出现在Firefox而不是其他浏览器中。Firefox处理 lastIndex RegExp对象的属性?

    除非您真的需要全局标志(在示例代码中似乎不是这样),否则我建议您不要使用它。

        2
  •  0
  •   jitter    15 年前

    看着你的regex我不明白你为什么要用 g 旗帜。你在用 ^ (线路起点)和 $ 不管怎样,在你的正则表达式中 是完全无用的,不会改变任何东西(除了在FF中破坏代码)。把它拿走就没事了。

    这是一个浏览器错误(我猜有些regex JIT优化出错了)。在4.x的betas中,这个错误对我来说不再发生了。

    如果你检查我的进一步减少 test case (在FF、Opera和Chrome的第四个输出中返回空值)并仔细阅读 RegExp.lastIndex 你会明白为什么会这样

    如果lastIndex等于字符串的长度,并且正则表达式与空字符串不匹配,则正则表达式与输入不匹配,lastIndex将重置为0。

    在第一次之后 log(b.exec(datestr)); b.lastIndex 是10,正则表达式与空字符串不匹配,因此调用 日志(b.exec(datestr)); 第二次失败了。