引言:从“拼接地狱”到优雅写法的进化之路
你有没有经历过这样的时刻?当你面对一段又臭又长的字符串拼接代码时,内心OS是:“这真的是我写的吗?”或者在处理数组数据的时候,用for循环一层套一层,最后自己都搞不清哪个变量对应哪个值了。
别担心,JavaScript 的 ES6(ECMAScript 2015)标准带来了两个非常实用的功能:字符串模板(Template Strings) 和 数组的 map() 方法。它们不仅能让我们的代码更加简洁易读,还能显著提升开发效率和可维护性。
今天我们就来聊聊这两个神器,看看它们是如何帮助我们告别“拼接地狱”,轻松实现 DOM 操作与数据转换的
一、字符串模板 —— 让字符串也能有灵魂
1.1 曾经的痛苦:字符串拼接的噩梦
在 ES6 出现之前,我们通常使用加号 + 来拼接字符串:
var name = '旺财';
var age = 2;
var sentence = "My dog is " + name + ". He is " + (age * 7) + " years old.";
这种写法虽然功能没问题,但一旦遇到复杂的 HTML 或者大量变量嵌入,就会变得异常丑陋,甚至容易出错。而且遇到大量的数据填充,就会变得异常麻烦,写不完的+号和""
比如你要输出一个结构比较复杂的 div:
var html = '<div class="user-card">' +
'<h3>' + user.name + '</h3>' +
'<p>来自:' + user.city + '</p>' +
'<p>年龄:' + user.age + '</p>' +
'</div>';
看得我真的头大,”香菇“
1.2 新时代的曙光:字符串模板登场!
ES6 给我们带来了一个全新的语法——字符串模板(Template Strings) ,它使用反引号(`)包裹整个字符串,并通过 ${} 插入变量或表达式。
举个栗子🌰:
const name = '旺财';
const age = 2;
const sentence = `My dog ${name} is ${age * 7} years old.`;
console.log(sentence); // 输出:My dog 旺财 is 14 years old.
对比上述代码是不是更简洁明朗多了
而且字符串模板还支持多行文本,再也不用手动添加 \n 或者反复拼接了:
const poem = `春眠不觉晓,
处处闻啼鸟。
夜来风雨声,
花落知多少。`;
这段诗如果用传统方式写,估计得拼上天 😅 想想都觉得头大
1.3 实战应用:DOM 中的字符串模板
在实际项目中,字符串模板最常用于动态生成 HTML 内容。比如我们要展示一个朋友列表:
const friends = [
{ name: '小娄', hometown: '抚州' },
{ name: '小王', hometown: '上饶' },
{ name: '小刘', hometown: '赣州' }
];
我们可以结合模板字符串和 DOM 操作,快速构建页面内容:
<ul id="friends"></ul>
<script>
const ul = document.querySelector('#friends');
let html = '';
for (let friend of friends) {
html += `
<li>
${friend.name} -
<i>${friend.hometown}</i>
</li>
`;
}
ul.innerHTML = html;
</script>
这样写是不是比以前清晰多了?而且格式也保留得很好,调试起来更方便,简单来说就是优雅哈哈哈哈
不过,如果你是一个追求极致优雅的程序员,那么接下来的内容会让你大呼过瘾,我们来告别传统的for循环,让代码更简洁
二、数组的 map() 方法 —— 数据变换的艺术
2.1 数组操作的痛点
假设你现在有一组 JSON 格式的数据,你想把它们转换成 HTML 元素插入到页面中。你会怎么做?
可能的做法是:
- 用
for循环遍历数组; - 每次取出一个元素;
- 拼接成 HTML 字符串;
- 最后赋值给 innerHTML。
没错,这确实能完成任务,但代码不够优雅,也不够现代
就如同下面的代码:
<ul id="friends"></ul>
<script>
// JSON 数组
// 数据
const friends = [
{ name: '小娄', hometown: '抚州' },
{ name: '小王', hometown: '上饶' },
{ name: '小刘', hometown: '赣州' }
]
// DOM 编程
const ul = document.querySelector('#friends')
// 如果不需要index(下标),for of 更语义化
// for (let friend of friends) {
ul.innerHTML += `
<li>
${friend.name} -
<i>${friend.hometown}</i>
</li>
`
}
看着就比之前字符串+拼接更好,但是有没有更优雅的方法呢?有的,有的,兄弟有的,比这这更优雅的方法还有9种(牢九门)哈哈哈哈,其实是还有遍历数组更优雅的方法的,下面我就来给大家介绍
2.2 map() 初体验:让数组自己“变魔术”
map() 是 ES6 中新增的一个数组方法,它的作用是对数组中的每一个元素进行一次“映射”操作,并返回一个新的数组。
举个简单的例子:
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8]
是不是很神奇?我们没有修改原数组,而是得到了一个新的数组
2.3 map() 在 DOM 编程中的实战
回到前面的朋友列表问题,我们可以用 map() 更优雅地处理:
const friends = [
{ name: '小娄', hometown: '抚州' },
{ name: '小王', hometown: '上饶' },
{ name: '小刘', hometown: '赣州' }
];
const ul = document.querySelector('#friends');
// 使用 map 把每个对象转成 li 元素字符串
const listItems = friends.map(friend => `
<li>
${friend.name} -
<i>${friend.hometown}</i>
</li>
`);
// 因为 map 返回的是数组,所以需要用 join('') 合并成一个字符串
ul.innerHTML = listItems.join('');
我们来分开解释一下map()和join()的作用
🔁 map() 之后变成了什么样?
map就是遍历每个数组元素然后通过回调函数修改内容返回,上述经过map()后其实就是返回以下内容这里的 map() 把原来的 JSON 数据(对象)转换成了 HTML 字符串。
[
`
<li>
小娄 -
<i>抚州</i>
</li>
`,
`
<li>
小王 -
<i>上饶</i>
</li>
`,
`
<li>
小刘 -
<i>赣州</i>
</li>
`
]
🔗 join('') 之后变成什么样?
因为我们传的是空字符串 '',所以相当于把所有元素直接连在一起,不加任何分隔符。
🧵 拼接后的结果如下:
<li>
小娄 -
<i>抚州</i>
</li>
<li>
小王 -
<i>上饶</i>
</li>
<li>
小刘 -
<i>赣州</i>
</li>
📌 总结一下流程:
| 步骤 | 内容 | 类型 |
|---|---|---|
| 原始数据 | JSON 数组 | Array<Object> |
| map() 后 | 字符串数组 | Array<String> |
| join('') 后 | 单个 HTML 字符串 | String |
| 最终赋值给 innerHTML | 插入到 DOM 中显示为列表 | 渲染 HTML |
💡 小贴士:为什么不用 forEach?
你可能会问:我也可以用 forEach 来拼接字符串啊,干嘛非要 map + join?
✅ 答案是:map() 更具语义化、更优雅、更函数式!
map()表达的是“把 A 转成 B”,非常清晰;forEach更像是过程式编程,需要手动拼接字符串,不够直观;map()返回数组,方便后续处理(比如过滤、排序等);join()又快又简洁,效率更高。
三、组合技 —— 模板字符串 + map() 的完美搭档
既然我们已经掌握了字符串模板和 map(),那不妨来点高级玩法,看看它们如何配合得更好
3.1 案例一:生成带序号的列表
const fruits = ['苹果', '香蕉', '橙子'];
const list = fruits.map((fruit, index) => `
<li>${index + 1}. ${fruit}</li>
`).join('');
document.getElementById('fruits').innerHTML = list;
在这个例子中,我们利用了 map() 的第二个参数 index,实现了自动编号的效果。
3.2 案例二:条件判断 + 模板字符串
有时候我们需要根据某些条件决定是否显示某个字段:
const users = [
{ name: 'Tom', isAdmin: true },
{ name: 'Jerry', isAdmin: false }
];
const userList = users.map(user => `
<div class="user">
<strong>${user.name}</strong>
${user.isAdmin ? '<span style="color:red">(管理员)</span>' : ''}
</div>
`).join('');
document.getElementById('users').innerHTML = userList;
这里我们使用了三元运算符,在模板字符串内部实现了条件判断,非常灵活,判断user.isAdmin是否存在,是不是能感受到它的魅力了呢
四、常见误区 & 最佳实践
4.1 不要用 innerHTML 滥用
虽然 innerHTML 很方便,但它也有风险,尤其是在用户输入未经过滤的情况下直接插入 HTML,可能会导致 XSS 攻击。因此建议:
- 如果只是插入纯文本,优先使用
textContent; - 如果必须使用 HTML,务必对内容进行转义或过滤。
本人之前学过一段时间的网络安全,深知xss带来的危害
XSS(Cross-Site Scripting)是一种网络安全漏洞,攻击者通过在网页中注入恶意脚本,欺骗用户的浏览器执行该脚本,从而执行未经授权的操作。XSS 攻击通常用于窃取用户的 cookie、会话令牌或其他敏感信息,甚至可以进行钓鱼攻击。
XSS的类型
XSS 主要分为三种类型:
存储型 XSS(Persistent XSS or Stored XSS):
攻击方式: 攻击者将恶意脚本永久地存储在目标网站的数据库、文件或服务器上。 例如,攻击者可能在评论区、论坛帖子或用户个人资料中提交包含恶意脚本的内容。
攻击过程: 当其他用户访问包含恶意脚本的页面时,脚本会被从服务器加载并执行。
危害: 危害最大,影响范围广,每次访问受影响页面的用户都会受到攻击。
示例: 一个评论系统,攻击者提交包含 的评论。所有浏览该评论的用户都会看到警告框。
反射型 XSS(Reflected XSS or Non-Persistent XSS):
攻击方式: 攻击者通过 URL 参数、表单提交等方式将恶意脚本注入到用户的请求中。服务器接收到请求后,在响应中将恶意脚本反射给用户。
攻击过程: 用户需要点击包含恶意脚本的链接或提交包含恶意脚本的表单,才能触发攻击。
危害: 危害相对较小,需要用户交互才能触发,但攻击者可以通过社会工程学手段诱骗用户点击。
示例: 一个搜索功能,攻击者构造包含 的搜索链接并发送给用户。用户点击该链接后,服务器将恶意脚本返回给用户浏览器执行。
DOM 型 XSS(DOM-based XSS):
攻击方式: 攻击者通过操作页面的 DOM (Document Object Model) 来注入恶意脚本。 恶意脚本并不需要经过服务器,而是在用户的浏览器中直接修改页面的 DOM 结构。
攻击过程: 恶意脚本通常存在于 URL 的片段标识符 (hash, #) 或查询参数中,然后通过 JavaScript 代码读取这些参数并修改 DOM 结构。
危害: 危害与反射型 XSS 类似,需要用户交互才能触发。
示例: 一个 JavaScript 代码读取 URL 的 hash 值,并将其作为 HTML 内容添加到页面中。攻击者可以构造包含恶意脚本的 hash 值,例如 #,从而注入恶意脚本。
这里以本人做过的CTF题目来详细的查看XSS的危害
到一个输入界面我们先测试有没有XSS漏洞
我们来测试一下
确实存在xss的漏洞
这时候攻击者可以利用xss平台来进行攻击
会采用xss平台生成的payload来注入到我们的网站,从而可以监控我们的网站
一但有人访问,就会被xss平台所检测
这样你就被其监控了,可以获取你的cookie,会话令牌或其他敏感信息
4.2 注意性能问题
频繁操作 DOM 会带来性能损耗。虽然上面的例子都是单次更新,但如果是在循环中频繁修改 DOM,比如:
for (let i = 0; i < 1000; i++) {
ul.innerHTML += `<li>Item ${i}</li>`;
}
这种写法会导致多次重绘重排,影响性能。正确的做法是先构建完整字符串,再一次性插入 DOM。
4.3 始终保持语义化和模块化
随着项目规模增大,建议将字符串模板和数据处理封装成函数或组件,提高复用性和可维护性:
function renderUserCard(user) {
return `
<div class="card">
<h3>${user.name}</h3>
<p>家乡:${user.hometown}</p>
</div>
`;
}
function renderUsers(users) {
return users.map(renderUserCard).join('');
}
结语:拥抱 ES6,让代码更有“人情味”
JavaScript 发展至今,已经从一门“玩具语言”成长为现代前端开发的核心力量。而 ES6 的出现,则让我们真正体会到了编程的优雅与乐趣。
字符串模板和 map() 方法,就像是一对默契的搭档,一个负责优雅地拼装内容,一个负责智能地处理数据。它们不仅提升了代码的可读性,也让我们的开发效率大大增强。
下次当你面对一堆乱七八糟的字符串拼接代码时,不妨试试这两个工具,相信我,你会爱上这种“丝滑”的感觉
作者寄语:
技术的本质是解决问题,而优秀的工具可以让问题变得更容易解决。愿你在编程的路上越走越远,写出既优雅又有灵魂的代码!