You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何移除sessionStorage后fields_entity仍能读取到旧值?

问题原因分析:表单重置后切换角色仍显示旧值

问题场景

  • 点击「Clear formulary」按钮触发resetForm(),该函数清空所有输入框值,并移除sessionStorage中存储实时数据的fields_entity项,函数内日志显示该项已为null
  • 切换#role下拉框选项时,已清空的输入框会被重新填入之前的旧值
  • 在#role的change事件触发的roleOptions()函数中,调用autosave时能读取到fields_entity的旧值,全程未刷新页面
  • 已找到解决方案,仅需理解该行为的根本原因,怀疑与autosave函数有关

相关代码

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Document</title>
</head>

<body>
    <form class="form user">
        <h2>Cadastrar <span></span></h2>

        <label for="role">Choose your role</label>
        <select id="role" name="role">
            <option value="teacher" name="teacher">Teacher</option>
            <option value="student" name="student">Student</option>
            <option value="adm" name="adm">Collaborator</option>
        </select>

        <label for="name">Full name</label>
        <input type="text" id="name" name="name">

        <label for="email">Email</label>
        <input type="email" id="email" name="email" novalidate>

        <label for="password">Password</label>
        <input type="password" id="password" name="password">
        <div class="strength-pwd"></div>

        <button type="submit">Register</button>
        <button type="button" onclick="resetForm();"> Clear formulary</button>
    </form>
    <script>
        function autosave(form, id) {
            let fields = sessionStorage.getItem(`fields_${id}`);

            fields ? (fields = JSON.parse(fields)) : (fields = {});


            Array.from(form.elements).forEach(input => {
                if (input.tagName === "INPUT" || input.tagName === "SELECT") {
                  
                    input.addEventListener("input", () => {
                        fields[input.name] = input.value;
                        sessionStorage.setItem(`fields_${id}`, JSON.stringify(fields));
                    });
                }
            });

            Object.keys(fields).forEach(key => {
                const input = document.querySelector(`input[name="${key}"], select[name="${key}"]`);

                if (input) {
                    input.value = fields[key];
                }
            });

        }
        const form = document.querySelector(".form.user");
        const role = document.querySelector("#role");

        function roleOptions() {
            autosave(form, "entity"); //Second check = {name : ..., email...};
            let roleValue = role.value;
            switch (roleValue) {
                case "student":
                    studentFormulary();
                    break;
                case "teacher":
                    teacherFormulary();
                    break;
                case "adm":
                    admFormulary();
                    break;
                default:
                    studentFormulary();
                    break;
            }

        };
        role.addEventListener("change", roleOptions);

        function resetForm() {

            form.querySelectorAll("input").forEach(e => { e.value = ""; });
            sessionStorage.removeItem("fields_entity"); //First check = null
        }
            
        function studentFormulary() {
        
//...
     

        }
        function teacherFormulary() {
            
//...
            
        }
        function admFormulary() {

//...
           
        }
        roleOptions();

    </script>
</body>

</html>

行为原因解析

1. 重复绑定事件监听器引发的闭包陷阱

每次调用autosave(form, "entity")(包括页面初始化、每次切换角色时),都会给所有input/select元素重复绑定新的input事件监听器。

第一次调用autosave时(页面加载完成执行roleOptions()),fields变量捕获了当时从sessionStorage读取的用户输入数据,后续这些旧的监听器会一直持有这个fields对象的引用——哪怕你在resetForm里删除了sessionStorage中的fields_entity项,这些旧监听器里的fields依然是最初的旧数据对象。

2. 事件触发与sessionStorage的读写时序问题

当你点击重置按钮后:

  • resetForm()清空输入框并删除sessionStorage的fields_entity,此时该项确实为null
  • 切换角色触发roleOptions(),首先调用autosave:
    • 第一步从sessionStorage读取到fields_entity为null,因此初始化fields为空对象
    • 但紧接着,函数又给输入元素绑定了新的input监听器,同时旧的监听器并未被移除
    • 切换下拉框的操作可能触发了旧监听器的执行(部分浏览器中select的change事件会触发绑定的input事件,或者监听器的触发时机存在延迟),旧监听器会把自己持有的旧fields数据重新写入sessionStorage
    • 最后autosave执行遍历赋值逻辑,从刚被旧监听器写回的sessionStorage中读取到旧值,重新填充到输入框里

3. autosave函数的设计缺陷

函数没有做事件监听器的去重处理,导致每次调用都会叠加新的监听器,而旧监听器持有的闭包变量不会被垃圾回收,始终保留着最初的旧数据引用,这是问题的核心根源。

内容的提问来源于stack exchange,提问作者Nikolas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 22:24:50