标签模板字面量

标签模板字面量

该方法可以在字符串模板之前加入函数,用来处理模板:

function foo(str, ...val) {
    console.log(str);
    console.log(val);
}
var desc = "awesome";

foo`Everything is ${desc}!`;
// ["Everything is ", "!"]
// ["awesome"]
str为字符串数组,val为所有变量数组

该方法可以用来过滤HTML字符串,防止恶意攻击

let sender = '<script>alert("abc")</script>'; // 恶意代码
let message =
  SaferHTML`<p>${sender} has sent you a message.</p>`;

function SaferHTML(templateData) {
  let s = templateData[0];
  for (let i = 1; i < arguments.length; i++) {
    let arg = String(arguments[i]);

    // Escape special characters in the substitution.
    s += arg.replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;");

    // Don't escape special characters in the template.
    s += templateData[i];
  }
  return s;
}

// <p>&lt;script&gt;alert("abc")&lt;/script&gt; has sent you a message.</p>

sender为用户输入的变量,该方法可以过滤掉用户输入的HTML标签

实现国际化 参考

let I18n = {
  use({locale, defaultCurrency, messageBundle}) {
    I18n.locale = locale;
    I18n.defaultCurrency = defaultCurrency;
    I18n.messageBundle = messageBundle;
    return I18n.translate;
  },

  translate(strings, ...values) {
    let translationKey = I18n._buildKey(strings);
    let translationString = I18n.messageBundle[translationKey];

    if (translationString) {
      let typeInfoForValues = strings.slice(1).map(I18n._extractTypeInfo);
      let localizedValues = values.map((v, i) => I18n._localize(v, typeInfoForValues[i]));
      return I18n._buildMessage(translationString, ...localizedValues);
    }

    return 'Error: translation missing!';
  },
  _localizers: {
    s /*string*/: v => v.toLocaleString(I18n.locale),
    c /*currency*/: (v, currency) => (
      v.toLocaleString(I18n.locale, {
        style: 'currency',
        currency: currency || I18n.defaultCurrency
      })
    ),
    n /*number*/: (v, fractionalDigits) => (
      v.toLocaleString(I18n.locale, {
        minimumFractionDigits: fractionalDigits,
        maximumFractionDigits: fractionalDigits
      })
    )
  },

  _extractTypeInfo(str) {
    let match = typeInfoRegex.exec(str);
    if (match) {
      return {type: match[1], options: match[3]};
    } else {
      return {type: 's', options: ''};
    }
  },

  _localize(value, {type, options}) {
    return I18n._localizers[type](value, options);
  },

  // e.g. I18n._buildKey(['', ' has ', ':c in the']) == '{0} has {1} in the bank'
  _buildKey(strings) {
    let stripType = s => s.replace(typeInfoRegex, '');
    let lastPartialKey = stripType(strings[strings.length - 1]);
    let prependPartialKey = (memo, curr, i) => `${stripType(curr)}{${i}}${memo}`;

    return strings.slice(0, -1).reduceRight(prependPartialKey, lastPartialKey);
  },

  // e.g. I18n._formatStrings('{0} {1}!', 'hello', 'world') == 'hello world!'
  _buildMessage(str, ...values) {
    return str.replace(/{(\d)}/g, (_, index) => values[Number(index)]);
  }
};

let name = 'Bob';
let amount = 1234.56;
let i18n;

let messageBundle_fr = {
  'Hello {0}, you have {1} in your bank account.': 'Bonjour {0}, vous avez {1} dans votre compte bancaire.'
};
let messageBundle_de = {
  'Hello {0}, you have {1} in your bank account.': 'Hallo {0}, Sie haben {1} auf Ihrem Bankkonto'
};
let messageBundle_zh_Hant = {
  'Hello {0}, you have {1} in your bank account.': '你好{0},你有{1}在您的銀行帳戶。'
};

i18n = I18n.use({locale: 'fr-CA', defaultCurrency: 'CAD', messageBundle: messageBundle_fr});
i18n`Hello ${name}, you have ${amount}:c in your bank account.`;
// => 'Bonjour Bob, vous avez 1 234,56 $CA dans votre compte bancaire.'

i18n = I18n.use({locale: 'de-DE', defaultCurrency: 'EUR', messageBundle: messageBundle_de});
i18n`Hello ${name}, you have ${amount}:c in your bank account.`;
// => 'Hallo Bob, Sie haben 1.234,56 € auf Ihrem Bankkonto.'

i18n = I18n.use({locale: 'zh-Hant-CN', defaultCurrency: 'CNY', messageBundle: messageBundle_zh_Hant});
i18n`Hello ${name}, you have ${amount}:c in your bank account.`;
// => '你好Bob,你有¥1,234.56在您的銀行帳戶。'

除此之外,你甚至可以使用标签模板,在 JavaScript 语言之中嵌入其他语言jsx具体实现

jsx`
  <div>
    <input
      ref='input'
      onChange='${this.handleChange}'
      defaultValue='${this.state.value}' />
      ${this.state.value}
   </div>
`

String.raw()

raw方法返回一个斜杠都会被在前面添加一个斜杠,用来转译字符串。如果原字符串已经被转译,则会再次转译。

String.raw`Hi\n${2+3}!`;
// 返回 "Hi\\n5!"

String.raw`Hi\u000A!`;
// 返回 "Hi\\u000A!"

String.raw`Hi\\n`
// 返回 "Hi\\\\n"

在实际项目中原本准备用此方法,用来处理emoji的unicode编码,但是在{}内的变量含有\时无法被转译,最后通过escape多写了一层算法进行处理。