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

JavaScript:查找与的连续匹配项。exec()

  •  0
  • Matthew  · 技术社区  · 7 年前

    我有一个具有无数属性的对象,例如颜色和品牌,用于描述产品。我正在寻找一种以段落形式动态生成产品描述的方法(因为API不提供),我想出了一种方法,通过编写括号中包含“道具”的“模板”来实现 {{}} . 我编写了一个函数,通过将“props”替换为键的值,在字符串中注入对象属性来“解析”模板。

    例如:

    对象: {color: 'white'}

    模板: "The bowl is {{color}}."

    结果: "The bowl is white."

    由于某种原因,我的解析函数不起作用。 {{general_description}} 未分析。

    var obj = {
      brand: "Oneida",
      general_description: "Plate",
      material: "China",
      color: "Bone White",
      product_width: "5\""
    };
    
    const templatePropRe = /{{(\w*)}}/g;
    const parse = (template) => {
      while ((result = templatePropRe.exec(template)) !== null) {
        let match = result[0],
          key = result[1];
        template = template.replace(match, obj[key]);
      }
      return template;
    }
    
    console.log(parse('This {{color}}, {{material}} {{general_description}} supplied by {{brand}} has a width of {{product_width}}.'));

    我遵循 MDN docs 在示例下>查找连续的匹配项。它表示我需要首先将正则表达式存储在变量中(例如。, templatePropRe ),因为表达式不能处于while循环条件中,否则将无限循环。然而,如果我这样做,我的问题就解决了。看见 here ...什么都没坏。

    我使用重写函数 String.prototype.match ,它按预期工作,但我无法访问捕获,因此我需要首先使用 stripBrackets . 请参见使用的工作示例 match here .

    我想知道的是为什么我的 parse() 使用的功能 RegExp.prototype.exec 是否正常工作?

    3 回复  |  直到 7 年前
        1
  •  2
  •   Serge K.    7 年前

    移除 /g 正则表达式中的标志。根据 documentation ,当此标志存在时,它将更新regex对象的 lastIndex 属性,该属性指示下一次调用的起始位置 exec() 将开始搜索匹配项。

    var obj = {
      brand: "Oneida",
      general_description: "Plate",
      material: "China",
      color: "Bone White",
      product_width: "5\""
    };
    
    const templatePropRe = /{{(\w*)}}/;
    const parse = (template) => {
      while ((result = templatePropRe.exec(template)) !== null) {
        let match = result[0],
          key = result[1];
          
        template = template.replace(match, obj[key]);
      }
      
      return template;
    }
    
    console.log(parse('This {{color}}, {{material}} {{general_description}} supplied by {{brand}} has a width of {{product_width}}.'));
        2
  •  1
  •   Artem Bozhko    7 年前

    之所以会发生这种情况,是因为您在代码中修改并检查了相同的字符串。 而regExp在每次执行后都会保存匹配子字符串的索引,您可以更改字符串的长度,并且regEx在下次执行时会从预期的其他点开始。

    var obj = {
      brand: "Oneida",
      general_description: "Plate",
      material: "China",
      color: "Bone White",
      product_width: "5\""
    };
    
    const templatePropRe = /{{(\w*)}}/g;
    const parse = (template) => {
      var resultStr = template;
      while ((result = templatePropRe.exec(template)) !== null) {
        let match = result[0],
          key = result[1];
        resultStr = resultStr.replace(match, obj[key]);
      }
      return resultStr;
    }
    
    console.log(parse('This {{color}}, {{material}} {{general_description}} supplied by {{brand}} has a width of {{product_width}}.'));
        3
  •  1
  •   Wiktor Stribiżew    7 年前

    您可以使用 callback method as a replacement argument inside a String#replace method . 这样,在每次匹配时将动态构造生成的字符串,使代码执行更快。

    请参见下面的修复示例:

    var obj = {
      brand: "Oneida",
      general_description: "Plate",
      material: "China",
      color: "Bone White",
      product_width: "5\""
    };
    const parse = (template) => {
      return template.replace(/{{(\w*)}}/g, ($0, $1) => obj[$1] ? obj[$1] : $0 );
      // ES5 way:
      // return template.replace(/{{(\w*)}}/g, function($0, $1) {
      //       return obj[$1] ? obj[$1] : $0;
      // });
    }
    
    console.log(parse('{{keep}} This {{color}}, {{material}} {{general_description}} supplied by {{brand}} has a width of {{product_width}}.'));

    请注意,在这里,找到匹配项后 ($0, $1) => obj[$1] ? obj[$1] : $0 代码执行以下操作:将整个匹配指定给 $0 变量,并将组1值指定给 $1 ; 然后,如果有一个名称为 $1 在里面 obj ,将把值而不是匹配放在结果字符串的正确位置。否则,整个比赛将被推迟(替换为 '' 如果要删除 {{...}} 具有不存在的密钥名称)。