ES6 tagged templates practical usability

前端 未结 3 2012
时光取名叫无心
时光取名叫无心 2020-11-27 21:24

I understand the syntax of ES6 tagged templates. What I don\'t see is the practical usability. When is it better than passing an object parameter, like the settings in jQuer

相关标签:
3条回答
  • 2020-11-27 21:39

    See Sitepoint's explanation:

    The final stage of template strings specification is about adding a custom function before the string itself to create a tagged template string.

    ...

    For instance, here is a piece of code to block strings that try to inject custom DOM elements:

    var items = [];
    items.push("banana");
    items.push("tomato");
    items.push("light saber");
    var total = "Trying to hijack your site <BR>";
    var myTagFunction = function (strings,...values) {
      var output = "";
      for (var index = 0; index < values.length; index++) {
        var valueString = values[index].toString();
    
        if (valueString.indexOf(">") !== -1) {
          // Far more complex tests can be implemented here :)
          return "String analyzed and refused!";
        }
    
        output += strings[index] + values[index];
      }
    
      output += strings[index]
      return output;
    }
    
    result.innerHTML = myTagFunction `You have ${items.length} item(s) in your basket for a total of $${total}`;
    

    Tagged template strings can used for a lot of things like security, localization, creating your own domain specific language, etc.

    0 讨论(0)
  • 2020-11-27 21:48

    They're useful because the function can (almost) completely define the meaning of the text inside it (almost = other than placeholders). I like to use the example of Steven Levithan's XRegExp library. It's awkward to use regular expressions defined as strings, because you have to double-escape things: Once for the string literal, and once for regex. This is one of the reasons we have regular expression literals in JavaScript.

    For instance, suppose I'm doing maintenance on a site and I find this:

    var isSingleUnicodeWord = /^\w+$/;
    

    ...which is meant to check if a string contains only "letters." Two problems: A) There are thousands of "word" characters across the realm of human language that \w doesn't recognize, because its definition is English-centric; and B) It includes _, which many (including the Unicode consortium) would argue is not a "letter."

    Suppose in my work I've introduced XRegExp to the codebase. Since I know it supports \pL (\p for Unicode categories, and L for "letter"), I might quickly swap this in:

    var isSingleUnicodeWord = XRegExp("^\pL+$"); // WRONG
    

    Then I wonder why it didn't work, *facepalm*, and go back and escape that backslash, since it's being consumed by the string literal:

    var isSingleUnicodeWord = XRegExp("^\\pL+$");
    // ---------------------------------^
    

    What a pain. Suppose I could write the actual regular expression without worrying about double-escaping?

    I can: With a tagged template function. I can put this in my standard lib:

    function xrex(strings, ...values) {
        const raw = strings.raw;
        let result = "";
        for (let i = 0; i < raw.length; ++i) {
            result += raw[i];
            if (i < values.length) { // `values` always has one fewer entry
                result += values[i];
            }
        }
        return XRegExp(result);
    }
    

    Or alternately, this is a valid use case for reduce, and we can use destructuring in the argument list:

    function xrex({raw}, ...values) {
        return XRegExp(
            raw.reduce(
                (acc, str, index) => acc + str + (index < values.length ? values[index] : ""),
                ""
            )
        );
    }
    

    And then I can happily write:

    const isSingleUnicodeWord = xrex`^\pL+$`;
    

    Example:

    // My tag function (defined once, then reused)
    function xrex({raw}, ...values) {
        const result = raw.reduce(
            (acc, str, index) => acc + str + (index < values.length ? values[index] : ""),
            ""
        );
        console.log("Creating with:", result);
        return XRegExp(result);
    }
    
    // Using it, with a couple of substitutions to prove to myself they work
    let category = "L";                // L: Letter
    let maybeEol = "$";
    let isSingleUnicodeWord = xrex`^\p${category}+${maybeEol}`;
    function test(str) {
        console.log(str + ": " + isSingleUnicodeWord.test(str));
    }
    test("Русский");  // true
    test("日本語");    // true
    test("العربية");  // true
    test("foo bar");  // false
    test("$£");       // false
    <script src="https://cdnjs.cloudflare.com/ajax/libs/xregexp/3.2.0/xregexp-all.min.js"></script>

    The only thing I have to remember now is that ${...} is special because it's a placeholder. In this specific case, it's not a problem, I'm unlikely to want to apply a quantifier to the end-of-input assertion, but that's a coincidence...

    0 讨论(0)
  • 2020-11-27 21:50

    You can use tagged templates to build APIs that are more expressive than regular function calls.

    For example, I'm working on a proof-of-concept library for SQL queries on JS arrays:

    let admins = sql`SELECT name, id FROM ${users} 
                     WHERE ${user => user.roles.indexOf('admin') >= 0}`
    

    Notice it has nothing to do with String interpolation; it uses tagged templates for readability. It would be hard to construct something that reads as intuitively with plain function calls - I guess you'd have something like this:

    let admins = sql("SELECT name, id FROM $users WHERE $filter",
      { $users: users, $filter: (user) => user.roles.contains('admin') })
    

    This example is just a fun side project, but I think it shows some of the benefits of tagged templates.

    Another example, maybe more obvious, is i18n - a tagged template could insert locale-sensitive versions of your input.

    0 讨论(0)
提交回复
热议问题