JavaScript箭头函数配合FormData与fetch的this上下文问题解决
问题:箭头函数下FormData与fetch()的正确使用方式
我有一段JavaScript fetch()代码,可向MySQL数据库发送数据并阻止页面硬刷新,后端用PHP编写,原本运行正常。但将submit事件处理函数改为ES6箭头函数后,FormData()对象出现问题:
- 把第3行的submit事件处理函数改为箭头函数
i.addEventListener('submit', (e) => { ...时,抛出错误Uncaught TypeError: Failed to construct 'FormData': parameter 1 is not of type 'HTMLFormElement',这明显和this上下文有关; - 若移除this作为FormData的参数,PHP端会报错
Undefined index: create-board-name('create-board-name'是提交按钮的name属性)。
原JavaScript代码
if (form) { form.forEach(i => { i.addEventListener('submit', function(e) { if (e.submitter && e.submitter.classList.contains('js-fetch-button')) { e.preventDefault(); var formData = new FormData(this); formData.set(e.submitter.name, e.submitter.value); formData.set('json_response', 'yes'); // 和下方PHP的JSON编码逻辑相关 fetch( theURL, { method: 'post', body: formData }) .then((response) => { if (response.status === 200) { response.json().then((json) => { // --- 后续逻辑 }); } }).catch((error) => { console.error(error); }) } }) }) }
PHP代码(非问题根源)
虽然我认为PHP不是问题所在,还是附上代码:创建新看板时,它会更新数据库并以JSON格式返回信息,方便创建后直接使用新看板数据。
// 插入数据库 $stmt = $connection->prepare( "INSERT INTO boards (board_name, user_id) VALUES (:board_name, :user_id )" ); $stmt->execute([ ':board_name' => $create_board_name, ':user_id' => $sessionId ]); // 以JSON格式返回数据 if(isset($_POST['json_response'])) { $board_stmt = $connection->prepare( "SELECT * FROM boards WHERE user_id = :user_id AND board_name = :board_name ORDER BY board_id DESC" ); $board_stmt -> execute([ ':user_id' => $sessionId, ':board_name' => $create_board_name ]); $board_row = $board_stmt->fetch(); header('Content-type: application/json'); echo json_encode($board_row); exit; }
解决方案
核心原因
箭头函数没有自己的this上下文,它会继承外层作用域的this。这里外层是forEach的箭头函数,this指向全局对象(非严格模式)或undefined(严格模式),不再指向触发submit事件的表单元素,所以new FormData(this)会报错。
两种可行的修正方法
方法1:从事件对象中获取表单元素
submit事件的e.target或e.currentTarget就是触发事件的表单元素,直接用它们替代原来的this即可:
if (form) { form.forEach(i => { i.addEventListener('submit', (e) => { // 改为箭头函数 if (e.submitter && e.submitter.classList.contains('js-fetch-button')) { e.preventDefault(); // 用e.currentTarget或e.target获取表单元素 var formData = new FormData(e.currentTarget); formData.set(e.submitter.name, e.submitter.value); formData.set('json_response', 'yes'); fetch( theURL, { method: 'post', body: formData }) .then((response) => { if (response.status === 200) { response.json().then((json) => { // --- 后续逻辑 }); } }).catch((error) => { console.error(error); }) } }) }) }
方法2:直接使用forEach循环中的表单变量i
因为forEach里的i就是当前遍历到的表单元素,直接传入FormData即可,这种方式不受事件函数类型影响:
if (form) { form.forEach(i => { i.addEventListener('submit', (e) => { // 改为箭头函数 if (e.submitter && e.submitter.classList.contains('js-fetch-button')) { e.preventDefault(); // 直接用循环中的i变量,它就是当前表单元素 var formData = new FormData(i); formData.set(e.submitter.name, e.submitter.value); formData.set('json_response', 'yes'); fetch( theURL, { method: 'post', body: formData }) .then((response) => { if (response.status === 200) { response.json().then((json) => { // --- 后续逻辑 }); } }).catch((error) => { console.error(error); }) } }) }) }
这两种方法都能正确获取表单数据,PHP端也能正常拿到create-board-name等参数,同时保留箭头函数的写法。
内容的提问来源于stack exchange,提问作者paulo77
相关产品推荐
相关产品推荐

