引言 ES6(ECMAScript 2015)引入了模板字符串(Template Literals),彻底改变了 JavaScript 中字符串的拼接方式。相比传统的引号字符串,模板字符串提供了多行文本、字符串插值、标签模板等强大功能,使代码更加简洁易读。本文将全面介绍模板字符串的特性和应用场景。
基础语法 定义方式 模板字符串使用反引号(`)包裹,而非单引号或双引号。
1 2 3 4 5 6 7 var name = "John" ;var greeting = "Hello, " + name + "!" ;let name = "John" ;let greeting = `Hello, ${name} !` ;
字符串插值 使用 ${expression} 在字符串中嵌入表达式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 let user = { name : "Alice" , age : 25 }; let message = `用户 ${user.name} 今年 ${user.age} 岁` ;let price = 100 ;let discount = 0.8 ;let finalPrice = `折后价: ${price * discount} 元` ;function formatMoney (amount ) { return `¥${amount.toFixed(2 )} ` ; } let total = `总计: ${formatMoney(199.9 )} ` ;let isVip = true ;let level = `会员等级: ${isVip ? 'VIP' : '普通' } ` ;
多行字符串 模板字符串天然支持多行,无需使用 \n 或字符串拼接。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 var html = "<div>\n" + " <h1>标题</h1>\n" + " <p>内容</p>\n" + "</div>" ; let html = ` <div> <h1>标题</h1> <p>内容</p> </div> ` ;let userId = 123 ;let query = ` SELECT u.id, u.name, u.email, o.order_count FROM users u LEFT JOIN ( SELECT user_id, COUNT(*) as order_count FROM orders GROUP BY user_id ) o ON u.id = o.user_id WHERE u.id = ${userId} ` ;
高级特性 嵌套模板 模板字符串可以嵌套使用,构建复杂的字符串结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 let items = ['苹果' , '香蕉' , '橙子' ];let list = ` <ul> ${items.map(item => ` <li>${item} </li> ` ).join('' )}</ul> ` ;let user = { name : 'Tom' , vip : true };let badge = ` <div class="user"> <span>${user.name} </span> ${user.vip ? ` <span class="vip-badge">VIP</span> ` : '' } </div> ` ;
原始字符串(String.raw) String.raw 可以获取模板字符串的原始形式,不处理转义字符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 let path = `C:\Windows\System32` ;console .log (path);let rawPath = String .raw `C:\Windows\System32` ;console .log (rawPath);let regex = String .raw `\d+\.\d+` ;function createPath (...segments ) { return segments.join (String .raw `\`); } let fullPath = createPath('C:', 'Users', 'Admin', 'Documents'); // 结果: "C:\Users\Admin\Documents"
标签模板(Tagged Templates) 基础概念 标签模板是模板字符串最强大的特性,允许通过一个函数处理模板字符串。
1 2 3 4 5 6 7 8 function tag (strings, ...values ) { return processedString; } let result = tag`Hello ${name} , you have ${count} messages` ;
参数解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 let name = "Alice" ;let count = 5 ;function myTag (strings, ...values ) { console .log (strings); console .log (values); let result = "" ; for (let i = 0 ; i < strings.length ; i++) { result += strings[i]; if (i < values.length ) { result += values[i]; } } return result; } let output = myTag`Hello ${name} , you have ${count} messages` ;
实用标签函数 1. 自动转义 HTML 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 function htmlEscape (strings, ...values ) { const escapeMap = { '&' : '&' , '<' : '<' , '>' : '>' , '"' : '"' , "'" : ''' , '/' : '/' }; function escape (str ) { return String (str).replace (/[&<>"'/]/g , char => escapeMap[char]); } return strings.reduce ((result, str, i ) => { return result + str + (values[i] ? escape (values[i]) : '' ); }, '' ); } let userInput = '<script>alert("xss")</script>' ;let safeHtml = htmlEscape`<div>${userInput} </div>` ;function safeHTML (strings, ...values ) { const escaped = values.map (v => String (v) .replace (/&/g , '&' ) .replace (/</g , '<' ) .replace (/>/g , '>' ) ); return strings.reduce ((acc, str, i ) => acc + str + (escaped[i] || '' ), '' ); }
2. 多语言国际化(i18n) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 const translations = { 'en' : { 'HELLO' : 'Hello {0}, you have {1} messages' }, 'zh' : { 'HELLO' : '你好 {0},你有 {1} 条消息' } }; function i18n (lang ) { return function (strings, ...values ) { const key = strings.join ('{}' ).toUpperCase (); const template = translations[lang][key] || key; return template.replace (/\{(\d+)\}/g , (match, index ) => { return values[index] !== undefined ? values[index] : match; }); }; } const t = i18n ('zh' );let name = '张三' ;let count = 3 ;let message = t`hello ${name} , you have ${count} messages` ;
3. 样式组件(CSS-in-JS) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 function css (strings, ...values ) { const styles = strings.reduce ((acc, str, i ) => { return acc + str + (values[i] || '' ); }, '' ); const className = `css-${hash(styles)} ` ; const styleSheet = document .createElement ('style' ); styleSheet.textContent = `.${className} { ${styles} }` ; document .head .appendChild (styleSheet); return className; } let primaryColor = '#1890ff' ;let buttonClass = css` background-color : ${primaryColor} ; color : white; padding : 8px 16px ; border : none; border-radius : 4px ; cursor : pointer; &:hover { opacity : 0.8 ; } ` ;
4. 日志格式化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function log (strings, ...values ) { const timestamp = new Date ().toISOString (); const message = strings.reduce ((acc, str, i ) => { return acc + str + (values[i] !== undefined ? values[i] : '' ); }, '' ); console .log (`[${timestamp} ] ${message} ` ); } let user = 'admin' ;let action = 'login' ;log`User ${user} performed ${action} ` ;
5. 查询参数构建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function query (strings, ...values ) { const params = []; let queryText = strings.reduce ((acc, str, i ) => { if (values[i] !== undefined ) { params.push (values[i]); return acc + str + `$${params.length} ` ; } return acc + str; }, '' ); return { text : queryText, values : params }; } let userId = 123 ;let status = 'active' ;let q = query` SELECT * FROM users WHERE id = ${userId} AND status = ${status} ` ;
实际应用场景 1. URL 构建 1 2 3 4 5 6 7 8 9 10 11 12 function buildURL (base, ...pathSegments ) { const paths = pathSegments.map (p => encodeURIComponent (p)); return `${base} /${paths.join('/' )} ` ; } let apiBase = 'https://api.example.com' ;let userId = 'user/123' ; let resource = 'profile' ;let url = buildURL (apiBase, 'v1' , 'users' , userId, resource);
2. GraphQL 查询构建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function gql (strings, ...values ) { const query = strings.reduce ((acc, str, i ) => { return acc + str + JSON .stringify (values[i]); }, '' ); return { query }; } let userId = 123 ;let fields = ['name' , 'email' , 'avatar' ];let query = gql` query { user( id : ${userId} ) { ${fields.join('\n ' )} } } ` ;
3. 代码生成 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 function generateComponent (strings, ...values ) { const [name, props] = values; return ` import React from 'react'; const ${name} = ({ ${props.join(', ' )} }) => { return ( ${strings[1 ]} ); }; export default ${name} ; ` .trim ();} let componentName = 'UserCard' ;let propList = ['name' , 'avatar' , 'onClick' ];let template = generateComponent` ${componentName} ${propList} <div className="user-card" onClick={onClick}> <img src={avatar} alt={name} /> <span>{name}</span> </div> ` ;
与 Cocos Creator 结合 游戏内文本显示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class GameText { static format (template, params ) { return template.replace (/\{(\w+)\}/g , (match, key ) => { return params[key] !== undefined ? params[key] : match; }); } static showDamage (damage, isCritical ) { const color = isCritical ? '#FF0000' : '#FFFFFF' ; const size = isCritical ? 48 : 32 ; return { text : `${damage} ` , style : `color: ${color} ; font-size: ${size} px;` }; } static showLevelUp (level, rewards ) { let rewardText = rewards.map (r => `${r.name} x${r.count} ` ).join (', ' ); return ` 恭喜升级到 ${level} 级! 获得奖励: ${rewardText} ` .trim (); } } let result = GameText .showLevelUp (10 , [ { name : '金币' , count : 1000 }, { name : '钻石' , count : 50 } ]);
多语言支持 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class I18n { constructor ( ) { this .locale = 'zh' ; this .messages = { 'zh' : { 'WELCOME' : '欢迎回来,{username}' , 'LEVEL_INFO' : '当前等级: {level},经验: {exp}/{maxExp}' }, 'en' : { 'WELCOME' : 'Welcome back, {username}' , 'LEVEL_INFO' : 'Level: {level}, EXP: {exp}/{maxExp}' } }; } t (key, params = {} ) { const template = this .messages [this .locale ][key] || key; return template.replace (/\{(\w+)\}/g , (match, key ) => { return params[key] !== undefined ? params[key] : match; }); } } const i18n = new I18n ();this .welcomeLabel .string = i18n.t ('WELCOME' , { username : player.name }); this .levelLabel .string = i18n.t ('LEVEL_INFO' , { level : player.level , exp : player.exp , maxExp : player.maxExp });
注意事项 1. 浏览器兼容性 1 2 3 4 5 6 7 8 9 let greeting = `Hello, ${name} !` ;var greeting = "Hello, " + name + "!" ;
2. 性能考虑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function buildLargeString (items ) { return items.map (item => `Item: ${item} ` ).join ('\n' ); const lines = []; for (const item of items) { lines.push ('Item: ' + item); } return lines.join ('\n' ); }
3. 安全注意 1 2 3 4 5 6 7 8 let userInput = '<script>alert("xss")</script>' ;let html = `<div>${userInput} </div>` ;let safeHtml = htmlEscape`<div>${userInput} </div>` ;
总结 模板字符串是 ES6 引入的重要特性,它带来了:
特性
传统方式
模板字符串
字符串拼接
+ 运算符
${} 插值
多行文本
\n 或拼接
原生支持
HTML 转义
手动处理
标签模板
代码可读性
较差
优秀
最佳实践:
优先使用模板字符串替代字符串拼接
利用标签模板处理安全问题
使用多行模板简化 HTML/XML 构建
在 Cocos Creator 等游戏引擎中用于动态文本生成
掌握模板字符串,可以让 JavaScript 代码更加现代、简洁和安全。