- 教程
- 文档
【免费下载链接】30-seconds-of-code
Coding articles to level up your development skills
HTML 表单是向用户收集输入的最基础、最推荐的方式,但真正把表单数据取回到 JavaScript 代码中,却常常让人绕弯路。本文以 30 seconds of code 仓库中的 form-to-object 技巧 为核心,讲解如何用FormData接口配合Object.fromEntries()一步得到普通对象,以及如何序列化成可直接拼接的查询字符串,并顺带覆盖编码边界、请求发送与相关扩展技巧。
核心思路:用 FormData 获取表单键值对
FormData接口提供了一种简洁的方式来构造一组代表表单字段及其值的键/值对(key/value pairs)。只要把表单元素的引用传给FormData()构造函数,浏览器就会自动扫描表单内所有带name属性的控件并收集它们的当前值:
const form = document.querySelector('#form'); const data = new FormData(form);这一步完成之后,data就是一组可迭代的键值对,键是字段的name属性值,值是对应的输入内容。值得注意的是,它收集的是控件当前的实时值,而不是初始的value属性,因此非常适合在submit事件或input事件中动态读取。
将表单数据转换为普通对象:formToObject
拿到FormData对象后,只需再用Object.fromEntries()转换一次,就能得到字段名作为键、字段值作为值的普通 JavaScript 对象。这正是仓库文档给出的完整实现:
const formToObject = form => Object.fromEntries(new FormData(form)); formToObject(document.querySelector('#form')); // { email: 'test@email.com', name: 'Test Name' }配合一个真实的表单来看,整个过程非常直观:
<form id="form"> <input type="text" name="name" value="Test Name" /> <input type="email" name="email" value="test@email.com" /> </form>const formToObject = form => Object.fromEntries(new FormData(form)); formToObject(document.querySelector('#form')); // { email: 'test@email.com', name: 'Test Name' }从实现原理看,Object.fromEntries()会把可迭代的键值对序列(iterable of key-value pairs)直接构造成对象,与Map、数组以及FormData都能自然衔接。仓库语言配置 content/languages/javascript.yaml 中把FormData、Object.fromEntries()等均登记为可检索的引用 API,足见这种组合是该技巧集合中的标准写法。
将表单数据序列化为查询字符串:serializeForm
如果需要的不是对象,而是把表单数据序列化(serialize)成查询字符串,只需要对前面的代码做几处调整。创建FormData对象后,用Array.from()得到条目数组,再利用其第二个参数指定每条条目如何映射:把键与值用=连接,并对每一部分应用encodeURIComponent()。最后用Array.prototype.join()以&作为分隔符拼接成查询字符串:
const serializeForm = form => Array.from(new FormData(form), field => field.map(encodeURIComponent).join('=') ).join('&'); serializeForm(document.querySelector('#form')); // 'email=test%40email.com&name=Test%20Name'为什么每个部分都要 encodeURIComponent
encodeURIComponent()会转义字符串中除A-Z a-z 0-9 - _ . ! ~ * ' ( )之外的所有字符。对于表单值这种只作为 URL 一部分的字符串,它是正确的选择。仓库的姊妹篇 encodeuri-encodeuricomponent.md 明确对比过两者的区别:
encodeURIComponent():适合编码 URL 的片段,会转义@、空格、&、=等保留字符,这正是查询参数所需的;encodeURI():适合编码完整 URL,会保留? : / @ & =等 URL 结构字符,若用它编码参数值会导致查询串结构被破坏。
因此示例输出中,test@email.com变成test%40email.com,Test Name中的空格变成%20——这正是标准的application/x-www-form-urlencoded编码方式,可直接用于 GET 请求或拼接到 URL 上。
实战:把表单数据用于请求与发送
两种函数各自适合不同的场景,综合使用可以覆盖从"本地拿到数据"到"发送给服务端"的完整链路:
// 场景一:读取表单值到对象,供前端逻辑使用(如表单预填、校验) const obj = formToObject(document.querySelector('#form')); // 场景二:把表单拼成查询字符串,追加到 URL 发起 GET 请求 const qs = serializeForm(document.querySelector('#form')); fetch(`/api/search?${qs}`);而在需要发送multipart/form-data(如同时携带文件上传)的场景,FormData对象本身可以直接作为fetch()的 body,浏览器会自动设置正确的Content-Type与边界:
document.querySelector('#form').addEventListener('submit', e => { e.preventDefault(); fetch('/api/upload', { method: 'POST', body: new FormData(e.target), }); });边界情况与注意事项
理解FormData的收集规则,可以避免踩坑:
- 必须有
name属性:没有name的控件不会出现在结果中; - 禁用控件被忽略:
disabled的输入框、下拉框不会被收集; - 未选中的复选框:不会被收集;选中的复选框才会以它的
value(缺省为"on")出现; - 重复的
name:同名的多个控件(如多选下拉框、同名 checkbox 组)会生成多个键值对,Object.fromEntries()只保留最后一个,而serializeForm的映射会全部保留; - 空值与空串:空输入框会以空字符串值出现,序列化时会编码为
key=; - 文件输入:
FormData中对应条目的值是File对象,直接转成普通对象或字符串会丢失文件内容,这类场景应保留FormData形态发送。
相关技巧延伸:查询字符串与对象互转
表单序列化得到的查询字符串,日后还需要解析回来。仓库中的 query-string-to-object.md 给出了对应的逆向操作:用String.prototype.split()切出?之后的部分,交给URLSearchParams()构造器,再用展开运算符与Object.fromEntries()还原成对象:
const queryStringToObject = url => Object.fromEntries([...new URLSearchParams(url.split('?')[1])]); queryStringToObject('https://google.com?page=1&count=10'); // {page: '1', count: '10'}反向的objectToQueryString则用Object.entries()配合Array.prototype.reduce()拼接,只在字符串非空时追加?,后续键值对用&连接:
const objectToQueryString = queryParameters => { return queryParameters ? Object.entries(queryParameters).reduce( (queryString, [key, val], index) => { const symbol = queryString.length === 0 ? '?' : '&'; queryString += typeof val === 'string' ? `${symbol}${key}=${val}` : ''; return queryString; }, '' ) : ''; }; objectToQueryString({ page: '1', size: '2kg', key: undefined }); // '?page=1&size=2kg'它与serializeForm相互呼应:一个负责"对象 → 查询字符串",一个负责"表单 → 查询字符串",再加上queryStringToObject的"字符串 → 对象",三者在实际项目中经常配合使用。
在仓库中的定位与继续学习
本文讲解的两种技巧位于仓库的 form-to-object.md,带有browser与object两个标签。它既属于 browser 浏览器技巧集合(该集合专注于 DOM 元素的选取、遍历与操作),也属于 object 对象技巧集合。如果想继续深入,仓库中还有大量相关姊妹篇可以对照阅读:
- encodeuri-encodeuricomponent.md:URL 编码的两个函数如何选择;
- query-string-to-object.md:查询字符串与对象互转;
- edit-url-params.md 与 construct-url.md:URL 参数的读取与改写。
掌握formToObject与serializeForm两个工具函数,就掌握了从"用户填写的表单"到"JavaScript 可操作的数据结构"之间最轻量的两条通路,足以覆盖绝大多数前端表单处理的日常需求。
- 教程
- 文档
【免费下载链接】30-seconds-of-code
Coding articles to level up your development skills
相关推荐
CANN ops-math 算子开发指南:aclnnLog 与 aclnnInplaceLog 自然对数算子接口详解与实战
CANN ops math 算子开发指南:aclnnLog 与 aclnnInplaceLog 自然对数算子接口详解与实战 导读 本文基于 CANN ops m
教程文档30 seconds of code 实战:JavaScript Map 与普通对象的双向转换
30 seconds of code 实战:JavaScript Map 与普通对象的双向转换 在 Map 与普通 JavaScript 对象(plain ob
教程文档30-seconds-of-code 实战:如何在 JavaScript 中深度冻结(Deep Freeze)对象
30 seconds of code 实战:如何在 JavaScript 中深度冻结(Deep Freeze)对象 导读 JavaScript 中的对象天然是可
教程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考