如何在JavaScript中验证电子邮件:从简单正则表达式到深入底层解析
\n\n每个开发者一生中至少都会遇到一个问题:“如何在JavaScript中验证电子邮件地址?”。这个问题在Stack Overflow上获得了数千次浏览和数百个回答,因为这个话题充满了细微差别。今天,我们不仅会提供现成的代码,还会深入底层:分析为什么简单的检查可能会漏掉a@b,或者阻止"test@[IPv6:2001:db8::1]"。开始吧!
为什么“仅仅用正则表达式”是个坏主意?
\n\n互联网上最流行的解决方案是复制一个正则表达式。但老实说:电子邮件地址就是一团乱麻。RFC 5321和RFC 5322规范允许使用+、-、.、%等字符,甚至允许括号内的注释。还有像museum或@[192.168.1.1]这样的域名。试着把所有这些东西塞进一行正则表达式——你要么会漏掉一个有效的地址,要么会阻止一个真实用户。
下面是一个在Stack Overflow上常见的例子:
\n\nconst emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n这段代码在90%的情况下有效,但是:
\n- \n
- 会漏掉
test@localhost(对本地网络有效) \n - 会阻止
"test@[192.168.1.1]"(根据RFC是有效的) \n - 不检查域名长度或点号的存在 \n
教训:不要试图编写完美的正则表达式。最好使用简单检查 + 服务器端验证的组合。
\n\n电子邮件地址的解剖:编写代码前需要了解的知识
\n\n在编写验证器之前,让我们根据规范来分析电子邮件的结构:
\n\n- \n
- 本地部分(@之前):可以包含字母、数字、点号、加号、连字符,但不能以点号开头或结尾。允许使用引号来表示特殊字符。 \n
- 域名(@之后):由点号分隔的标签组成。每个标签长度为1到63个字符。域名可以是方括号内的IP地址。 \n
- 总长度:不超过254个字符。 \n
了解了这些,我们就可以编写更有意义的检查了。
\n\n步骤1:简单但有效的验证(适用于99%的情况)
\n\n我们从生产项目中使用的代码开始(例如,在Angular或React中)。它并不完美,但对于大多数表单来说已经足够了:
\n\nfunction isEmailSimple(email) {\n // 去除空格\n const trimmed = email.trim();\n \n // 检查空字符串\n if (!trimmed) return false;\n \n // 基本结构:某物 @ 某物 . 某物\n const atIndex = trimmed.lastIndexOf('@');\n if (atIndex < 1 || atIndex === trimmed.length - 1) return false;\n \n const localPart = trimmed.slice(0, atIndex);\n const domain = trimmed.slice(atIndex + 1);\n \n // 域名必须至少包含一个点号\n const dotIndex = domain.lastIndexOf('.');\n if (dotIndex < 1 || dotIndex === domain.length - 1) return false;\n \n // 检查长度\n if (trimmed.length > 254) return false;\n if (localPart.length > 64) return false;\n \n return true;\n}\n\n// 示例:\nconsole.log(isEmailSimple('user@example.com')); // true\nconsole.log(isEmailSimple('a@b.c')); // true(形式上有效)\nconsole.log(isEmailSimple('user@localhost')); // false(没有点号)\nconsole.log(isEmailSimple('@example.com')); // false\n\n\n这段代码已经比盲目的正则表达式好多了。它检查了结构、长度以及@和点号的存在。但它会漏掉user@[192.168.1.1]——对于大多数网页表单来说,这并不关键。
步骤2:考虑RFC的高级验证(几乎和浏览器一样)
\n\n现代浏览器通过type="e属性使用内置验证。