拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

彻底搞懂 JavaScript 中单引号、双引号、反引号的区别与用法

彻底搞懂 JavaScript 中单引号、双引号、反引号的区别与用法

最近在代码评审里看到一个挺典型的场景:新来的同事写前端组件,一会儿用'用户名: ' + name拼接,一会儿用`${name} 已登录`插值,还有一段 HTML 字符串里单引号双引号缠在一起,跑起来没问题,但是看得人头大。他问我:"这三个引号到底该用哪个?都有什么区别?" 我当时意识到,很多刚接触 JavaScript 的同学,对单引号、双引号、反引号的认识其实停留在"能包住字符串就行"的层面,但实际用起来,转义、插值、换行、嵌套这些坑一个接一个。

这篇文章我就把 JavaScript 里单引号'和反引号`的区别、使用场景、注意事项系统梳理一遍。我会用对比表、核心示例和避坑指南三种形式来讲,目标是让新手也能一眼看懂,同时把背后"为什么这样设计"的逻辑也讲清楚。如果你已经在写业务代码但偶尔被引号搞晕,这篇同样值得花十分钟看完。

1. 三种引号的身份定位:单双引号是一家,反引号是另一套体系

很多新手容易把"单引号"和"反引号"理解成同一种东西的两种写法,就像全角和半角一样。这是最大的误解。在 JavaScript 里,单引号'和双引号"才是真正的"孪生兄弟",而反引号`创建的是模板字符串(Template Literal),它从 ES6 开始引入,是一套完全不同的字符串体系。

1.1 单引号与双引号:除了外观,本质完全相同

JavaScript 的字符串类型只有一种,就是 String,没有"单引号字符串"和"双引号字符串"之分。'abc'和"abc"在语法层面完全等价,内存结构、运行效率也完全一致。

那为什么前端圈子里单引号更流行?主要原因是历史习惯:前端代码经常要和 HTML 打交道,而 HTML 属性习惯用双引号包裹,比如<div class="card">。如果 JS 字符串也用双引号,拼接 HTML 时就得转义:

// 双引号拼 HTML,class 属性的双引号必须转义 const html = "<div class=\"card\">" + content + "</div>"; // 单引号拼 HTML,class 属性的双引号直接写 const html = '<div class="card">' + content + '</div>';

所以 Airbnb 等主流前端风格指南很早就推荐使用单引号——不是为了性能,单纯是为了减少转义、提升可读性。这个背景到现在依然成立,只不过有了模板字符串之后,这种拼接场景已经被替代掉大半了。

1.2 反引号不是"另一种引号",而是模板字符串

反引号`在键盘左上角,它创建的字符串有一个专属名字:模板字符串。它带来了三个普通字符串没有的能力:变量/表达式插值(${})、多行书写、嵌套引用。这三个能力让它在拼接动态文本、写 HTML 模板、构造复杂字符串时成为利器。

举个最直观的例子:

const name = '张三'; const age = 25; // 普通字符串拼接 const s1 = '用户名: ' + name + ',年龄: ' + age + '岁'; // 模板字符串插值 const s2 = `用户名: ${name},年龄: ${age}岁`;

你会发现模板字符串更像是在"填空",而不是"拼接"。这个设计思路来自很多模板引擎(比如 Mustache、Handlebars),只是 ES6 直接把它内置进了语言本身。所以记住一句话:单引号和双引号是"普通字符串",反引号是"模板字符串"。理解了这句话,后面的所有规则都顺理成章。

2. 一表对比:单引号、双引号、反引号的边界到底在哪

标题里提到的"对比表"我放在这一节。先看核心能力对比,再按实际使用场景给出一份选型参考。

2.1 核心能力对比表

对比维度单引号''双引号""反引号``
本质普通字符串字面量普通字符串字面量模板字符串
变量插值不支持不支持支持,语法${}
多行书写不支持不支持支持,直接换行
内部出现同种引号需转义\'需转义\"需转义\`
内部出现异种引号双引号无需转义单引号无需转义单双引号都无需转义
表达式计算不支持不支持支持${}内写任意 JS 表达式
适用范围简单固定文本简单固定文本动态文本、模板、嵌套场景

这张表里的关键信息有两个:一是单引号和双引号每行都一样,它们之间没有任何能力差异;二是反引号在插值、多行、表达式三栏直接"碾压",同时也有自己独特的转义要求——它需要转义的不是单引号也不是双引号,而是反引号本身。

2.2 实际使用场景选型表

场景推荐写法理由
固定文案、错误提示单引号简洁,无插值需求
HTML 属性拼接单引号包 HTML避免转义双引号
动态拼接变量模板字符串${}可读性远超加号拼接
多行文本、模板片段模板字符串直接换行,不用写\n
JSON 字符串互相转换单引号/双引号模板字符串不参与序列化,别误用
正则表达式/.../字面量引号和反引号都不是正则专用语法

选型的核心逻辑是:有动态内容和结构性换行就选模板字符串,没有就用单引号。这个原则能覆盖绝大多数日常场景。至于双引号,目前的主要存在感是:某些工具生成代码(比如 Vue/React 脚手架里)默认输出双引号,以及 JSON 格式规定必须用双引号(但 JSON 是数据格式,不是 JS 代码,两者不要混淆)。

3. 核心示例拆解:插值、多行、表达式,模板字符串的三大用法

这一节是"核心示例"部分。我会从三个最常用的维度展开,每个维度都配上可以直接复制运行的代码。

3.1 插值替代加号拼接

早期 ES5 时代,动态字符串只能靠加号拼接,变量一多就变成"+++"地狱:

const user = { name: '李四', age: 30, city: '上海' }; // 加号拼接:需要手动处理空格和标点 const info = '姓名: ' + user.name + ',年龄: ' + user.age + ',城市: ' + user.city; // 模板字符串:结构一目了然 const info2 = `姓名: ${user.name},年龄: ${user.age},城市: ${user.city}`;

注意${}里不止能放变量,任何合法的 JavaScript 表达式都能放进去。比如三元运算:

const score = 85; const result = `成绩: ${score >= 60 ? '及格' : '不及格'}(${score}分)`; console.log(result); // 成绩: 及格(85分)

我见过很多入门教程只讲${变量},没提"表达式"三个字,导致读者以为里面只能放变量。实际上函数调用、数组取值、数学计算都没问题:

const items = [3, 5, 7]; const message = `总和: ${items.reduce((a, b) => a + b, 0)},最大值: ${Math.max(...items)}`;

3.2 多行字符串:直接换行,告别\n

普通字符串里写多行文本,要么用\n拼接,要么用数组join:

// 普通字符串多行 const lines = '第一行\n第二行\n第三行'; // 数组 join 法 const lines2 = ['第一行', '第二行', '第三行'].join('\n');

模板字符串直接回车换行即可:

const lines3 = `第一行 第二行 第三行`;

输出结果完全一样,都是三行文本。这个特性在写组件模板、生成邮件内容、拼 SQL 片段时非常实用。但要注意一点:字符串里的换行和缩进会被原样保留。也就是说,如果你在模板字符串前面加了缩进,输出里也会有这些空格:

// 注意左侧的缩进会被保留 const html = ` <div> <p>内容</p> </div> `; console.log(html); // 输出: // (换行) // <div> // <p>内容</p> // </div>

这在后面"避坑指南"里还会再提到——它是模板字符串多行特性的双刃剑。

3.3 嵌套模板:模板里套模板

模板字符串的另一个隐藏能力是嵌套。因为${}内部可以写任何表达式,而模板字符串本身也是一个表达式,所以可以套上一层:

const name = '王五'; const role = '管理员'; const badge = `[${role}]`; // 嵌套模板 const welcome = `欢迎 ${`${name}${badge}`} 登录系统`; console.log(welcome); // 欢迎 王五[管理员] 登录系统

实际业务里,这种两层嵌套在动态生成复杂文本时挺常见。比如根据用户状态生成标签,再拼进大模板里。但我不建议嵌套超过两层——一旦超过,可读性会急转直下,不如拆成中间变量:

const userName = '赵六'; const statusText = `当前状态: ${userName ? '在线' : '离线'}`; const finalText = `【监控】${statusText}`;

4. 转义与嵌套规则:引号的"套娃"游戏怎么玩

引号最让人头疼的地方是转义。我见过不少同事写代码的时候为了转义的事反复删除重写,其实规则并不复杂,掌握规律就好。

4.1 普通字符串:遇到同种引号才需要转义

单引号字符串里出现单引号,必须用反斜杠转义;出现双引号则不需要:

const s1 = 'It\'s a book'; // 需要转义 const s2 = "It's a book"; // 双引号包裹,内部单引号不用转义

反过来,双引号字符串里出现双引号要转义,出现单引号不用:

const s3 = "He said \"hello\""; // 需要转义 const s4 = 'He said "hello"'; // 单引号包裹,内部双引号不用转义

有一个很实用的技巧:当字符串内容里同时出现单引号和双引号时,选一种作为边界,另一种留在内容里。比如"我说'你好'然后说"再见""这种句子,用反引号包裹就是最省力的选择,见下一节。

4.2 反引号:单双引号都不怕,只怕反引号自己

模板字符串内部出现单引号和双引号时,完全不需要任何转义:

const text = `他说:"你好",我回答:'Hello!'`; console.log(text); // 他说:"你好",我回答:'Hello!'

模板字符串内部要是想出现反引号,就必须转义(用\`):

const code = `console.log(\`hello world\`)`; console.log(code); // console.log(`hello world`)

同理,如果你需要在字符串里输出${}字面量,也要转义美元符号,写法是\${:

const tpl = `模板语法示例: \${name}`; console.log(tpl); // 模板语法示例: ${name}

这个场景在写文档、生成代码片段、做模板引擎教学时经常会碰到,忘了转义就会得到意想不到的插值结果。

4.3 实际工作里最高频的引号混用:HTML 和事件字符串

前端开发中引号嵌套最密集的地方,是用模板字符串生成 HTML。事件处理函数里的字符串往往要再套一层引号:

const userId = 42; const removeBtn = ` <button onclick="removeUser(${userId})">删除</button> `; function removeUser(id) { console.log('删除用户:', id); }

这里你看到的是一次"三层引号套娃":最外层是模板字符串反引号,HTML 属性用双引号,onclick里的 JavaScript 字符串其实不用再包引号了,因为${userId}已经直接插入了数值。如果你在${}里放的是对象属性,也要注意属性引号位置,稍不留神就会形成属性嵌套错误。

我们再对比一下不用模板字符串时这个 HTML 有多别扭:

const removeBtn = '<button onclick="removeUser(' + userId + ')">删除</button>';

对比之下,模板字符串在"HTML 模板"场景下的优势是碾压性的。

5. 避坑指南:六个新手高频翻车现场

这一节是全文的"避坑指南"核心。以下六个坑,前四个是我在评审和教学里反复看到的新手错误,后两个是团队协作里常见的"慢性病"。

5.1 坑一:模板字符串里写${}却原样输出了

很多新手一开始会把模板字符串当成"自动插入语法",但写出来的代码是这样的:

const name = '小明'; console.log(`Hello, {name}`); // 错!没有 $ 符号,插值不生效 console.log(`Hello, ${name}`); // 对!输出 Hello, 小明

插值语法必须包含完整的$和花括号{},少了$就只是普通文本。这个坑的变种是:你想在输出里保留${name}这个"模板字面量"给后端做二次渲染,结果直接写了\${name}然后看到它原样出来了,这个是对的;但如果你没转义,就会变成插值,输出了变量的实际值。做模板引擎或代码生成器时尤其要小心。

5.2 坑二:把模板字符串当 JSON 序列化工具

普通字符串可以用在 JSON 的存储、传输,但模板字符串本身不参与 JSON 序列化。有人用模板字符串去构造 JSON,结果多出换行或插值错误:

const obj = { name: '小红', age: 18 }; // 错误示范 const jsonString = `{"name": "${obj.name}", "age": ${obj.age}}`; // 虽然这里结果碰巧能用,但稍复杂就会因逗号、冒号、空格问题解析失败 // 正确做法:用 JSON.stringify const jsonString2 = JSON.stringify(obj);

如果确实需要手写 JSON 片段,模板字符串也能用,但必须严格控制格式,并且用JSON.parse校验。注意 JSON 规定键和字符串值必须是双引号,所以模板字符串写 JSON 时仍然会充满双引号——这在 JS 代码里需要用反引号或单引号包裹整体,引号层级会比较密,建议直接用JSON.stringify处理。

5.3 坑三:多行缩进导致输出带一坨空格

模板字符串的多行特性很好用,但缩进、换行全部保留,这在生成 HTML 或日志时会带来意外:

// 看起来整齐,输出却带缩进 const list = ` <ul> <li>苹果</li> <li>香蕉</li> </ul> `; console.log(list); // 输出会包含每行开头的两个空格

解决方案有三种:一是不在模板字符串内做缩进,把内容顶格写;二是输出前用.trim()去掉首尾空白;三是用数组join代替多行字符串。我自己写组件模板时,通常会让模板代码顶格,外部变量正常缩进,这样既保证代码可读性,又不污染输出。

5.4 坑四:构造正则时混淆引号和正则字面量

JavaScript 里正则表达式有专用的字面量写法/.../,它不是字符串,不需要也不能用引号包裹。但动态构造正则时,需要把模式串作为字符串传给new RegExp,这时候就容易跟引号体系搞混:

const prefix = 'user_'; const pattern = new RegExp(`^${prefix}\\d+$`); console.log(pattern.test('user_007')); // true

注意这里有个细节:模板字符串里写\\d是为了在生成的字符串中保留\d,如果你只写一个反斜杠\d,字符串解析时反斜杠会被丢弃,正则就会变成d+,匹配纯字母,直接翻车。所以动态正则这块,建议先console.log打印一下模式串,确认反斜杠还在,再去做匹配。

5.5 坑五:缩进、空格、中文引号混入

中文输入法下打出的引号是‘’和“”,而不是 JS 的'和"。这个问题看着低级,但坑起人来很隐蔽:因为中英文引号在外观上差异很小,报错信息却比较诡异。比如:

const a = 'hello’; // 这段代码会因为中英文引号混用、分号也是中文的,直接 SyntaxError

我在排查同事代码时遇到过好几次这种问题,看起来拼写完全没问题,一查才发现引号或分号是中文全角。建议编辑器开启引号高亮和字符检查,或者用 ESLint 的quotes规则兜底。这个坑和单引号/反引号本身关系不大,但因为它披着"引号问题"的外衣出现,必须单独提醒。

5.6 坑六:团队代码里三种引号混用,风格割裂

单引号派、双引号派、反引号派在同一份代码里"打架",是协作项目最常见的慢性病。一份代码里一会儿'字符串',一会儿"字符串",一会儿`字符串`包了个寂寞。代码不会出错,但可维护性会变差,review 时大家精力都耗在讨论引号上。

我建议团队直接引入 Prettier 或 ESLint 的quotes规则,统一默认单引号,只有需要插值、多行时才让开发者主动写模板字符串。这样既能保持代码风格统一,又不会限制模板字符串的使用。ESLint 配置大概是这样:

// .eslintrc.js module.exports = { rules: { quotes: ['error', 'single'], }, };

至于为什么默认单引号而不是双引号——回到第一节说的,前端生态里单引号在 HTML 拼接场景下转义最少,Airbnb 风格指南也这么推荐。如果你团队之前强制双引号,那保持一致也行,关键是不要三套混用。

6. 团队规范与我的选型经验

前面把技术细节讲完了,这一节聊点更偏工程实践的内容:引号选型不是"对不对"的问题,而是"省不省心"的问题。

6.1 主流规范是怎么选引号的

目前前端生态的引号规范大致是:Airbnb Style Guide 推荐单引号,Prettier 默认输出双引号(可配置为单引号),ESLint 的quotes规则可以强制单引号或双引号。如果你用npx prettier格式化代码,默认会把单引号改成双引号;如果你用 Vite 脚手架创建项目,生成的代码里也是单双引号混杂。所以团队必须有一个明确的约定,并让格式化工具自动执行,而不是靠人肉 review。

一个可行的落地组合是:Prettier + ESLint,Prettier 负责格式化,ESLint 负责兜底检查;提交代码前自动执行lint-staged,规则冲突时在仓库里统一配置。这套组合拳打下来,引号之争基本从日常消失在视野里。

6.2 我自己项目里的默认方案

做了这么多年开发,我的默认规则非常简单,可以分享出来直接抄:

  • 固定文本、对象键名、简单字符串 → 单引号;
  • 需要拼接变量、表达式、多行 → 模板字符串;
  • 双引号只在两种场景出现:JSON 字符串里(那是格式要求),或者内容里同时包含单双引号时作为外层边界。

这个方案的好处是规则够少,脑子不用记太多东西。模板字符串的使用也不需要刻意节制,但要注意别"为了反引号而反引号"——一个没有插值、没有换行的纯模板字符串,比如`hello world`,我个人觉得不如直接用单引号,因为会让读代码的人产生"这里是不是有插值"的预期。

6.3 新手最容易忽略的一件事

最后分享一个实操建议:写 JS 代码时,多花一秒确认当前输入的引号是英文还是中文。这个建议听起来很基础,却是新手点背的头号原因。我在带人时甚至让他们先在编辑器里把"自动更正引号"关掉,再用全英文输入法写代码。等引号、分号这些基础不再出错,再开始研究单引号、反引号在复杂场景下的取舍,你会有种"突然想通了"的感觉。

我个人在实际操作中的体会是:单引号和反引号之争,本质上是"普通字符串"和"模板字符串"之争。能用模板字符串的场景,就用它来减少拼接和转义;不需要模板能力的场景,老老实实单引号。如果你能把这一条内化成直觉,代码的可读性会提升一个档次,review 时也能少听几句"这里为什么要用反引号"的提问。

返回列表