string.match() 返回一个字符串而不是一个数组

4
我一直在使用Javascript的string.match(*regex*)函数来解析navigator.userAgent字符串。
根据MDN和W3schools的说明:
"match() 方法用于检索字符串中指定的正则表达式的匹配项。返回一个由匹配结果组成的数组对象。"
我首先进行一次解析,以获取所需的基本字符串:
var userAgent = navigator.userAgent;
var splitted = userAgent.match(/[(][^)]+[)]|\w+\/\S+/ig);

以下是我的输出结果(数组形式):
Mozilla/5.0
(Macintosh; Intel Mac OS X 10_10_3)
AppleWebKit/537.36
(KHTML, like Gecko)
Chrome/41.0.2272.76
Safari/537.36

然后我用for循环解析Navigator/Version类型的字符串。

for (var i = 0; i < splitted.length; i++ ) {
    var str = splitted[i];
    if (str[0] == '(') {
    } else {
        var name = str.match(/[^\/]+/i);
        var version = str.match(/[0-9|\.]+/i);
}

然而,令人惊讶的是,尽管我得到了期望的结果,我得到了一个字符串对象作为名称和一个数组对象作为版本

这怎么可能呢?

以下是代码片段(fiddle):

var userAgent = navigator.userAgent;
var splitted = userAgent.match(/[(][^)]+[)]|\w+\/\S+/ig);
var outputDiv = document.getElementById("log");

for (var i = 0; i < splitted.length; i++ ) {
  var str = splitted[i];
  if (str[0] == '(') {
  } else {
    var name = str.match(/[^\/]+/i);
    var version = str.match(/[0-9|\.]+/i);
    outputDiv.innerHTML += name.toString() + " is a " + typeof(name) + "<br>";
    outputDiv.innerHTML += version.toString() + " is a " + typeof(version) + "<br>";
  }
};
<div id="log"></div>

--- 更新 ---

感谢 FactoryAidan 的回答,问题出在作用域上。

结论:在命名全局变量时要小心 :)


11
开始使用MDN查看JavaScript文档。 - epascarello
4
没有办法一个匹配会返回一个字符串。 - Amit Joki
1
离题文化建议:在英语中,使用“亲爱的”一词来称呼非常正式的信件,并用来指代我们的丈夫/妻子。 ;) 我已经编辑掉了有点恶心的开头语。 - Jamiec
2
.match() 只能返回 Arraynull - Cᴏʀʏ
1
没有 /g 标志,match 返回捕获组而不是每个匹配项。例如:"test".match(/te(st)/)["test", "st"]。这里是一个元素的数组,但仍然是一个数组。 - Ry-
显示剩余7条评论
2个回答

5

全局变量作用域

你的变量名是name,这是一个全局浏览器窗口变量,本质上是一个字符串,不能作为数组存储。

即使你使用var name =重新声明它,你仍然处于全局作用域。因此,name(也就是window.name)只保留你给它赋的最后一个值。

你可以在没有定义任何变量的空白页面上使用以下代码进行测试:

console.log(name===window.name) // Returns true
console.log(name,window.name)   // Returns 'Safari Safari' for my browser

name改为其他名称

如果你将name变量改为另一个名称,比如my_name,它会将.match()的结果存储为数组。

var my_name = str.match(/[^\/]+/i);
var version = str.match(/[0-9|\.]+/i);

console.log(typeof my_name, my_name instanceof Array) // Returns object, true

通过包装函数来更改作用域

这是您的精确代码,被包装在一个函数中并返回正确的变量类型:

function getBrowserStuff(){

    var userAgent = navigator.userAgent;
    var splitted = userAgent.match(/[(][^)]+[)]|\w+\/\S+/ig);

    for (var i = 0; i < splitted.length; i++ ) {
        var str = splitted[i];
        if (str[0] == '(') {
        } else {
            var name = str.match(/[^\/]+/i);
            var version = str.match(/[0-9|\.]+/i);
            console.log('Name','Typeof '+(typeof name), 'IsArray '+(name instanceof Array),name)
            console.log('Version','Typeof '+(typeof version),'IsArray '+(version instanceof Array),version)
        }
    }

    return 'whatever'
}

getBrowserStuff()

将变量name更改为my_name OR像上面的函数一样包装代码会返回以下内容:

Name    Typeof object IsArray true ["Mozilla"]
Version Typeof object IsArray true ["5.0"]
Name    Typeof object IsArray true ["AppleWebKit"]
Version Typeof object IsArray true ["600.3.18"]
Name    Typeof object IsArray true ["Version"]
Version Typeof object IsArray true ["8.0.3"]
Name    Typeof object IsArray true ["Safari"]
Version Typeof object IsArray true ["600.3.18"]

之前它返回了这个:
Name    Typeof string IsArray false Mozilla
Version Typeof object IsArray true  ["5.0"]
Name    Typeof string IsArray false AppleWebKit
Version Typeof object IsArray true  ["600.3.18"]
Name    Typeof string IsArray false Version
Version Typeof object IsArray true  ["8.0.3"]
Name    Typeof string IsArray false Safari
Version Typeof object IsArray true  ["600.3.18"]

1
不要改变变量名,最好将代码包装在一个函数中。反正它已经用 var 声明了。 - Bergi
你可能没有做对,我通过将“name”更改为“my_name”并获取正确的变量类型来编辑了你的代码。请查看我的更新答案。 - FactoryAidan
1
有趣,+1。具体问题在于 window.name 有一个存储字符串化值的 setter。由于该属性是可配置的,因此可以通过 Object.defineProperty 绕过它。但最好避免使用全局变量。 - Oriol

0

这是不可能的,或者是你的实现存在错误。

根据ECMAScript 5.1规范,match的行为如下:

15.5.4.10 String.prototype.match (regexp)

当调用match方法并传入参数regexp时,将执行以下步骤:

  1. 调用CheckObjectCoercible并将当前值作为其参数。
  2. S成为调用ToString的结果,将当前值作为其参数。
  3. 如果Type(regexp)是对象且regexp的[[Class]]内部属性的值为“RegExp”,则让rxregexp
  4. 否则,让rx成为一个新的RegExp对象,就像通过表达式new RegExp(regexp)创建一样,其中RegExp是具有该名称的标准内置构造函数。
  5. global成为调用rx的[[Get]]内部方法的结果,参数为“global”。
  6. exec成为标准内置函数RegExp.prototype.exec请参见15.10.6.2)。
  7. 如果global不是true,则
    1. 返回调用exec的[[Call]]内部方法的结果,参数为Srx作为this值。
  8. 否则,globaltrue
    1. 调用rx的[[Put]]内部方法,参数为“lastIndex”和0。
    2. A成为一个新的数组,就像通过表达式new Array()创建一样,其中Array是具有该名称的标准内置构造函数。
    3. previousLastIndex为0。
    4. n为0。
    5. lastMatchtrue
    6. 重复,当lastMatchtrue
      1. result成为调用rx的[[Call]]内部方法的结果,参数为Srx作为this值。
      2. 如果resultnull,则将lastMatch设置为false
      3. 否则,result不为null
        1. thisIndex成为调用rx的[[Get]]内部方法的结果,参数为“lastIndex”。
        2. 如果thisIndex = previousLastIndex,则
          1. 调用rx的[[Put]]内部方法,参数为“lastIndex”和thisIndex+1。
          2. previousLastIndex设置为thisIndex+1。
        3. 否则,将previousLastIndex设置为thisIndex
        4. matchStr成为调用result的[[Get]]内部方法的结果,参数为“0”。
        5. 使用ToString(n)、Property Descriptor{[[Value 因此,对于您的全局正则表达式,唯一可能返回的值是null或数组A
          对于非全局正则表达式,调用{{link1:RegExp.prototype.exec}}的结果会被返回。但它也会返回null或数组:

          对字符串string执行正则表达式匹配并返回包含匹配结果的数组对象,如果string没有匹配,则返回null。


谢谢你的回答,但我不确定是否理解:是不可能的还是需要使用非全局正则表达式来解释? - Elie Zgala

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接