前端开发中的存储、表单与 this 指向深度解析
从 localStorage 到 redis,从 form 默认提交到 fetch/AJAX,从 this 的四种绑定到箭头函数的 Lexical Scope ------ 一篇打通前端核心知识体系
📌 前言
在日常前端开发中,我们经常面临几个经典问题:
- 数据应该存到哪里?浏览器缓存、localStorage、还是云端?
- 表单提交为什么页面会刷新?如何用 JS 优雅地发送请求?
this到底指向谁?为什么箭头函数可以"继承"外层的 this?
这篇文章将围绕存储技术 、表单提交演进 、this 指向规则 三条主线展开,并结合一个完整的 LocalStorage Todo 示例,帮助你建立系统的认知框架。
一、存储的"金字塔":从前端到云端
现代应用的数据存储是一个分层架构,每一层都有其适用场景。
1.1 浏览器缓存(HTTP Cache)
打开之前访问过的页面时加载极快,这是因为浏览器利用了强缓存或协商缓存。
- 强缓存:直接从磁盘/内存读取,不请求服务器。
- 协商缓存:向服务器验证资源是否过期。

1.2 本地存储(LocalStorage / SessionStorage)
适合存储少量键值对数据(通常 5-10MB),数据持久化在浏览器中。
javascript
// 存储
localStorage.setItem('username', '张三');
// 读取
const name = localStorage.getItem('username');
// 删除
localStorage.removeItem('username');
// 清空
localStorage.clear();
适用场景:用户偏好设置、草稿箱、购物车临时数据。
1.3 云端存储(Cloud Storage)
将文件(JSON、CSV、Excel、图片等)上传至 OSS(对象存储服务),适合需要跨设备、跨用户共享的数据。
优势:容量大、可扩展、支持 CDN 加速。
1.4 Redis:高性能 KV 缓存
Redis 是内存数据库,读写速度极快(微秒级)。常用于缓存数据库查询结果,减轻 MySQL 等持久化存储的压力。
javascript
// 伪代码示例
// 第一次访问:从 MySQL 读取文章列表,存入 Redis
const articles = await mysql.query('SELECT * FROM articles');
await redis.set('articles:list', JSON.stringify(articles));
// 后续访问:直接从 Redis 读取
const cached = await redis.get('articles:list');
1.5 LLM 向量存储(Embedding)
在 AI 应用中,文本通过 embedding 模型转化为向量,存入向量数据库(如 Pinecone、Milvus),用于语义检索。
与 Redis 的区别:Redis 存精确键值,向量数据库存高维向量,支持相似度搜索。
二、表单提交的演进:从同步到异步
2.1 传统表单提交(刷新页面)
html
<form action="/submit" method="POST">
<input name="username" />
<button type="submit">提交</button>
</form>
点击 submit 后,浏览器会向 action 地址发送请求,并刷新页面。用户体验较差,因为页面会闪烁,状态丢失。
2.2 AJAX / Fetch:无刷新提交
使用 JavaScript 拦截表单提交事件,通过 fetch 或 XMLHttpRequest 异步发送数据。
javascript
document.querySelector('form').addEventListener('submit', async (e) => {
e.preventDefault(); // 阻止默认刷新行为
const formData = new FormData(e.target);
const response = await fetch('/api/submit', {
method: 'POST',
body: formData,
});
const result = await response.json();
console.log('提交成功', result);
});
优势:页面无刷新,用户体验流畅,可局部更新 UI。

三、this 的完整指南(含 6 种场景)
this 是函数运行时动态绑定的执行上下文,与函数声明位置无关,只与调用方式有关。
3.1 普通函数调用(非严格模式)
javascript
function show() {
console.log(this); // 非严格模式 → window,严格模式 → undefined
}
show();
启用严格模式避免污染全局:
javascript
'use strict';
function show() {
console.log(this); // undefined
}
show();
3.2 作为对象的方法调用
javascript
const obj = {
name: '金小轩',
say() {
console.log(this.name); // '金小轩'
}
};
obj.say();
3.3 引用式赋值丢失 this
javascript
const obj = {
name: '金小轩',
say() {
console.log(this.name);
}
};
const fn = obj.say; // 引用式赋值
fn(); // undefined(非严格模式下 this 指向 window)
解决方案 :使用 bind 绑定 this。
3.4 作为构造函数调用
javascript
function Person(name) {
this.name = name;
}
const p = new Person('明明');
console.log(p.name); // '明明'
构造函数中的 this 指向新创建的实例对象,并且会自动返回该对象(除非显式返回其他对象)。
3.5 事件处理函数
javascript
document.querySelector('.link').addEventListener('click', function(e) {
console.log(this); // 指向 .link 元素
e.preventDefault();
});
展示事件冒泡与捕获阶段,this 在事件处理中的指向
3.6 手动指定 this(call / apply / bind)
| 方法 | 执行时机 | 传参方式 |
|---|---|---|
call |
立即执行 | 逐个传入 |
apply |
立即执行 | 数组形式传入 |
bind |
返回新函数,不立即执行 | 逐个传入(柯里化) |
javascript
const obj = { name: '明明' };
function speak(a, b) {
console.log(this.name, a, b);
}
speak.call(obj, 'hello', 'world'); // 明明 hello world
speak.apply(obj, ['hello', 'world']); // 明明 hello world
const bound = speak.bind(obj, 'hello');
bound('world'); // 明明 hello world
3.7 箭头函数:没有自己的 this
箭头函数不绑定 this,它会继承外层作用域的 this(Lexical Scope)。
javascript
const obj = {
name: '姆巴佩',
say() {
console.log(this.name); // '姆巴佩'(this 指向 obj)
setTimeout(() => {
console.log(this.name); // '姆巴佩'(箭头函数继承外层 this)
}, 1000);
}
};
obj.say();
如果使用普通函数:
javascript
setTimeout(function() {
console.log(this.name); // undefined 或 window.name
}, 1000);
结论 :在需要保留外层 this 的场景(如定时器、事件回调)中,箭头函数是更优雅的选择。

四、综合实战:LocalStorage Todo 应用
下面是一个完整的"Tapas 清单"示例,结合了 LocalStorage 存储 、表单异步提交 、事件处理与 this 绑定。
4.1 HTML 结构
html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LocalStorage Tapas</title>
<link rel="stylesheet" href="./common.css">
</head>
<body>
<div class="wrapper">
<h2>🥘 LOCAL TAPAS</h2>
<p>你的 Tapas 清单</p>
<ul class="plates">
<li>Loading Tapas...</li>
</ul>
<form class="add-items">
<input type="text" name="item" placeholder="添加一道 Tapas" required>
<input type="submit" value="添加">
</form>
<a href="https://www.baidu.com" class="link">去百度</a>
</div>
<script src="./common.js?v=4"></script>
</body>
</html>
4.2 JavaScript 逻辑(common.js)
javascript
'use strict';
// 1. DOM 引用
const oForm = document.querySelector('.add-items');
const oList = document.querySelector('.plates');
// 2. 从 localStorage 读取数据
function loadItems() {
const data = localStorage.getItem('tapas');
return data ? JSON.parse(data) : [];
}
// 3. 渲染列表
function render(items) {
oList.innerHTML = items.map((item, index) => `
<li>
<input type="checkbox" data-index="${index}" ${item.done ? 'checked' : ''}>
<span>${item.text}</span>
</li>
`).join('');
}
// 4. 存储到 localStorage
function saveItems(items) {
localStorage.setItem('tapas', JSON.stringify(items));
}
// 5. 添加新项(使用 bind 绑定 this)
function AddItem(e) {
e.preventDefault(); // 阻止表单默认提交
const input = this.querySelector('input[name="item"]');
const text = input.value.trim();
if (!text) return;
const items = loadItems();
items.push({ text, done: false });
saveItems(items);
render(items);
input.value = ''; // 清空输入框
}
// 6. 绑定事件(使用 bind 手动指定 this 为表单元素)
oForm.addEventListener('submit', AddItem.bind(oForm));
// 7. 处理 checkbox 变更(事件委托)
oList.addEventListener('click', function(e) {
const checkbox = e.target.closest('input[type="checkbox"]');
if (!checkbox) return;
const index = parseInt(checkbox.dataset.index);
const items = loadItems();
items[index].done = checkbox.checked;
saveItems(items);
render(items);
});
// 8. 初始化
render(loadItems());
// 9. 阻止百度链接跳转(演示 e.preventDefault)
document.querySelector('.link').addEventListener('click', function(e) {
e.preventDefault();
console.log('链接被拦截,不跳转');
});
五、关于版本号与缓存更新
在引入外部资源时,我们常使用 ?v=版本号 来强制浏览器加载最新文件,避免缓存旧版本。
html
<script src="./common.js?v=4"></script>
每次修改 JS 后,递增版本号即可让用户获取最新代码,无需手动清除缓存。
六、总结
| 知识点 | 核心要点 |
|---|---|
| 存储体系 | 浏览器缓存(快)→ LocalStorage(持久)→ Redis(极速 KV)→ 向量数据库(AI) |
| 表单提交 | 默认提交会刷新页面 → 使用 e.preventDefault() + fetch 实现无刷新 |
| this 绑定 | 调用方式决定 this:方法调用 → 对象;事件 → 元素;构造函数 → 实例;call/apply/bind 手动指定 |
| 箭头函数 | 没有自己的 this,继承外层作用域,适合回调与定时器 |
| LocalStorage | 存储键值对,容量约 5-10MB,适合小型持久化数据 |
💡 思考题 :如果要在多个浏览器标签页之间同步数据,localStorage 能做到吗?如果不能,有什么替代方案?(提示:可以搜索
storage事件或BroadcastChannel)
希望这篇文章能帮你建立起前端存储、表单交互与 this 指向的完整认知体系。如果觉得有用,欢迎点赞、收藏、转发!
