【问题标题】:Efficient Javascript String Replacement高效的 Javascript 字符串替换
【发布时间】:2010-09-27 12:53:29
【问题描述】:

您好,我有一个 HTML 块,我将重复使用(在用户访问期间的不同时间,而不是一次)。我认为实现这一点的最佳方法是创建一个 HTML div,将其隐藏,并在需要时获取其 innerHTML 并对几个关键字执行 replace()。以 HTML 块为例...

<div id='sample'>
  <h4>%TITLE%</h4>
  <p>Text text %KEYWORD% text</p>
  <p>%CONTENT%</p>
  <img src="images/%ID%/1.jpg" />
</div>

用动态数据替换这些关键字的最佳方法是去...

template = document.getElementById('sample');
template = template.replace(/%TITLE%/, some_var_with_title);
template = template.replace(/%KEYWORD%/, some_var_with_keyword);
template = template.replace(/%CONTENT%/, some_var_with_content);
template = template.replace(/%ID%/, some_var_with_id);

感觉就像我选择了一种愚蠢的方式来做到这一点。有没有人对如何以任何方式更快、更智能或更好地做到这一点有任何建议?此代码将在用户访问期间相当频繁地执行,有时每 3-4 秒执行一次。

提前致谢。

【问题讨论】:

    标签: javascript string performance replace


    【解决方案1】:

    您似乎想使用模板。

    //Updated 28 October 2011: Now allows 0, NaN, false, null and undefined in output. 
    function template( templateid, data ){
        return document.getElementById( templateid ).innerHTML
          .replace(
            /%(\w*)%/g, // or /{(\w*)}/g for "{this} instead of %this%"
            function( m, key ){
              return data.hasOwnProperty( key ) ? data[ key ] : "";
            }
          );
    }
    

    代码说明:

    • 期望 templateid 是现有元素的 id。
    • 期望data 是包含数据的对象。
    • 使用两个参数进行替换:
    • 第一个是搜索所有%keys%(或{keys},如果您使用备用版本)的正则表达式。键可以是 A-Z、a-z、0-9 和下划线 _ 的组合。
    • 第二个是一个匿名函数,每次匹配都会被调用。
    • 匿名函数在数据对象中搜索正则表达式找到的键。 如果在数据中找到键,则返回键的值,该值将替换最终输出中的键。如果未找到密钥,则返回一个空字符串。

    模板示例:

    <div id="mytemplate">
      <p>%test%</p>
      <p>%word%</p>
    </div>
    

    调用示例:

    document.getElementById("my").innerHTML=template("mytemplate",{test:"MYTEST",word:"MYWORD"});
    

    【讨论】:

    • 谢谢,这太棒了。我正准备在我的应用程序中包含一个像“jQuery printf”这样的插件,但这就是我真正需要的:-)
    • 除了!它无法插入数字零!替换函数应该真正检查值是否为空/未定义,而不是检查真值。
    • rescdsk:你说得对,它无法插入任何虚假值,如 0、NaN、false、null 和 undefined。我已更新代码以在对象上使用 hasOwnProptery。如果属性存在,它将被包含(甚至未定义)。如果该属性不存在,那么它将是空白空间。您可以通过在最后一个 "" 之间插入文本来将其更改为您想要的任何内容。
    • 这甚至是一个很棒的 oneliner:document.getElementById('templateid').innerHTML.replace(/%(\w*)%/g, (m, key) =&gt; data.hasOwnProperty(key) ? data[key] : "")
    • 感谢这个小脚本。让我头疼不已。
    【解决方案2】:

    您可能可以修改此代码以执行您想要的操作:

    let user = {
        "firstName": "John",
        "login": "john_doe",
        "password": "test",
    };
    
    let template = `Hey {firstName},
        
        You recently requested your password.
        login: {login}
        password: {password}
        
        If you did not request your password, please disregard this message.
        `;
    
    template = template.replace(/{([^{}]+)}/g, function(keyExpr, key) {
        return user[key] || "";
    });
    

    您可能还想查看JavaScriptTemplates

    【讨论】:

    • 完美满足我的需要——感谢分享。
    • 为了避免在处理函数中额外的替换调用,只需对正则表达式匹配进行分组: textbody.replace(/{([^{}]+)}/g, function(textMatched, key) { ....
    【解决方案3】:

    Template Replacement

    一个快速简单的解决方案是使用String.prototype.replace 方法。
    它需要一个可以是值或函数的第二个参数:

    function replaceMe(template, data) {
      const pattern = /{\s*(\w+?)\s*}/g; // {property}
      return template.replace(pattern, (_, token) => data[token] || '');
    }
    

    ###示例

    const html = `
      <div>
        <h4>{title}</h4>
        <p>My name is {name}</p>
        <img src="{url}" />
      </div>
    `;
    
    const data = {
      title: 'My Profile',
      name: 'John Smith',
      url: 'http://images/john.jpeg'
    };
    

    然后这样称呼它:

    replaceMe(html, data);
    

    【讨论】:

    • 这是解决这个问题最正确、最有效的方法。两个注意事项:[1] 将正则表达式更改为/\{\s*(\w+?)\\s*}/g,因为您可能只想接受类似变量的键并忽略括号中的任何空格。 [2] 您必须在空字符串(data[token]||'')中添加一个回退到data[token],因为可能存在数据对象不包含找到的键的情况,在这种情况下JS会输出字符串undefined .我会相应地更改您的答案。
    • @SlavikMeltser 这真的是解决这个问题最正确、最有效的方法吗?您是否看过 10 多年前在此线程中编写的stackoverflow.com/a/378001/36866,它使用相同的原理但没有后备错误?如果 data[token] 是数字零,它将是一个带有您建议的空字符串。
    • @SlavikMeltser,从未说过这是“最正确和最有效的方法”,而只是为这一挑战提供了“快速简便的解决方案”。我们的解决方案确实非常相似(最初没有注意到),但是我提供了一个强大的选项,可以在不同的场景中使用。希望这是有道理的。
    • @some 核心,假设数据以字符串形式提供,仅与假设data 是一个对象相同。在大多数情况下,这是可行的。这是因为,此解决方案的主要目的是在模板机制中使用它。这意味着 '0' 作为字符串仍然是正数。但是,你是对的,如果你想让它更加健壮,那么除了hasOwnProperty 之外,还有更多的功能需要添加,比如检查template 是否甚至是一个字符串,或者data 是一个对象,等等。这就是它的美妙之处,你总是有更多的空间来改进。
    • @SlavikMeltser 你是对的。这只是一个简单的字符串替换,从未打算成为像 Mustache、Handlebars 或 EJS 这样的全功能模板引擎。
    【解决方案4】:

    我怀疑会有什么更有效的。另一种方法是将其分成几部分然后连接,但我认为这不会有多大效率。考虑到每个连接都会产生一个与其操作数大小相同的新字符串,甚至可能更少。

    补充:这可能是最优雅的写法。此外——你在担心什么?内存使用情况?它很丰富,而且 Javascript 有一个不错的内存管理器。执行速度?那么你必须有一些巨大的字符串。恕我直言,这很好。

    【讨论】:

    • 感谢您的回复。实际上,这是一个更大的块,有更多的替换,所以在我开始之前,我想确保没有我遗漏的东西。再次感谢。
    • 还有更好的实现方式。
    【解决方案5】:

    你的方法是实现穷人模板系统的标准方法,所以没问题。

    不妨看看一些 JavaScript 模板库,例如 JST

    【讨论】:

      【解决方案6】:

      您可以通过链接替换而不是进行所有这些临时分配来提高效率。

      with(document.getElementById('sample'))
      {
        innerHTML = innerHTML.replace(a, A).replace(b, B).replace(c, C); //etc
      }
      

      【讨论】:

      • 也许,但这不会使可读性变差吗?虽然您可能会垂直堆叠这些调用...
      • 如果将关键字替换为也是对象属性的变量名(例如“id”),则将其放入 with 块中也会中断。
      • sigh - 外观性能相同,因为链接您创建对象但不分配它。对于 N 长的链,您可以保存 N-1 个分配。如果您在 with 范围内声明了属性,则将其放入 with 块中肯定会中断,但我假设按照 OP,他 没有这样做
      • @annakata,我的基准测试显示没有区别,你的显示一个吗?既然在 JS 中赋值只是创建一个引用,为什么它的时间应该是 non-neglibel?
      • 终于有人在js中使用了with(),听说用with()不好用,因为“不推荐使用with语句,因为它可能是混淆bug和兼容性的来源问题。有关详细信息,请参阅下面“描述”部分中的“歧义反对”段落。在“developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
      【解决方案7】:

      如果您愿意使用Prototype library,他们有很好的内置模板功能。

      看起来像:

      element.innerHTML = (new Template(element.innerHTML)).evaluate({
          title: 'a title',
          keyword: 'some keyword',
          content: 'A bunch of content',
          id: 'id here'
      })
      

      如果您在循环中运行代码,这将特别好,因为创建 JSON 对象/Javascript 对象文字很容易。

      不过,我预计速度不会提高。

      此外,您需要将分隔符样式更改为 #{keyword} 而不是 %keyword%

      【讨论】:

        【解决方案8】:

        这种方法生成可以缓存的函数模板:

        function compileMessage (message) {
        
          return new Function('obj', 'with(obj){ return \'' +
            message.replace(/\n/g, '\\n').split(/{{([^{}]+)}}/g).map(function (expression, i) {
              return i%2 ? ( '\'+(' + expression.trim() + ')+\'' ) : expression;
            }).join('') + 
          '\'; }');
        
        }
        
        var renderMessage = compileMessage('Hi {{ recipient.first_name }},\n\n' +
        
        'Lorem ipsum dolor sit amet...\n\n' +
        
        'Best Regarts,\n\n' +
        
        '{{ sender.first_name }}');
        
        
        renderMessage({
          recipient: {
            first_name: 'John'
          },
          sender: {
            first_name: 'William'
          }
        });
        

        返回:

        "Hi John,
        
        Lorem ipsum dolor sit amet...
        
        Best Regarts,
        
        William"
        

        【讨论】:

          【解决方案9】:

          Mustachejs 非常适合真正优雅的模板:

          <div id='sample'>
            <h4>{{TITLE}}</h4>
            <p>Text text {{KEYWORD}} text</p>
            <p>{{CONTENT}}</p>
            <img src="images/{{ID}}/1.jpg" />
          </div>
          

          然后您可以像这样使用模板:

          var template = document.getElementById(templateid).innerHTML;
          var newHtml = Mustache.render(template, {
              TITLE: some_var_with_title,
              KEYWORD: some_var_with_keyword,
              CONTENT: some_var_with_content,
              ID: some_var_with_id
          });
          document.getElementById('sample').innerHTML = newHtml;
          

          如果您从 Ajax 调用返回 JSON,这尤其适用 - 您可以直接将其传递给 Mustache.render() 调用。

          轻微的变化允许在每个浏览器或服务器上运行相同的模板。详情请见https://github.com/janl/mustache.js

          【讨论】:

            【解决方案10】:

            试试这个:http://json2html.com/

            它也支持复杂的 JSON 对象。

            【讨论】:

              【解决方案11】:
              var template = "<div id='sample'><h4>%VAR%</h4><p>Text text %VAR% text</p><p>%VAR%</p><img src="images/%VAR%/1.jpg" /></div>";
              
              var replace = function(temp,replace){
              temp = temp.split('%VAR%');
              for(var i in replace){
                        if(typeof temp[i] != 'undefined'){
                          temp[i] = temp[i] + replace[i];
                        }
                      }
                 return temp.join('');
              }
              
              replace(template,['title','keyword','content','id'])
              

              【讨论】:

              • 请在您的回答中添加一些解释
              • 你的引号坏了。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2015-03-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-05-06
              • 1970-01-01
              相关资源
              最近更新 更多