The Frontend Stack Nobody Teaches Together: Storage, Async Forms, and `this`
In-Depth Analysis of Storage, Forms, and the this Keyword in Frontend Development
From localStorage to Redis, from default form submission to fetch/AJAX, from the four
thisbinding rules to the lexical scope of arrow functions — mastering the core frontend knowledge system in one article.
📌 Preface
In daily frontend development, we often face several classic questions:
- Where should data be stored? Browser cache, localStorage, or the cloud?
- Why does the page refresh when a form is submitted? How can we elegantly send requests with JavaScript?
- What does
thispoint to? Why can arrow functions "inherit" the outerthis?
This article will expand on three main threads: storage technology, the evolution of form submission, and the rules of this binding, combined with a complete LocalStorage Todo example to help you build a systematic cognitive framework.
1. The Storage "Pyramid": From Frontend to Cloud
Data storage in modern applications is a layered architecture, with each layer having its applicable scenarios.
1.1 Browser Cache (HTTP Cache)
Previously visited pages load extremely fast because the browser uses strong caching or negotiation caching.
- Strong Cache: Reads directly from disk/memory without requesting the server.
- Negotiation Cache: Verifies with the server whether the resource has expired.
1.2 Local Storage (LocalStorage / SessionStorage)
Suitable for storing small amounts of key-value pair data (typically 5-10MB), with data persisted in the browser.
// Store
localStorage.setItem('username', 'Zhang San');
// Read
const name = localStorage.getItem('username');
// Delete
localStorage.removeItem('username');
// Clear
localStorage.clear();
Applicable Scenarios: User preference settings, draft boxes, temporary shopping cart data.
1.3 Cloud Storage
Upload files (JSON, CSV, Excel, images, etc.) to OSS (Object Storage Service), suitable for data that needs to be shared across devices and users.
Advantages: Large capacity, scalable, supports CDN acceleration.
1.4 Redis: High-Performance KV Cache
Redis is an in-memory database with extremely fast read and write speeds (microsecond level). It is often used to cache database query results, reducing the pressure on persistent storage like MySQL.
// Pseudocode example
// First access: Read article list from MySQL, store in Redis
const articles = await mysql.query('SELECT * FROM articles');
await redis.set('articles:list', JSON.stringify(articles));
// Subsequent access: Read directly from Redis
const cached = await redis.get('articles:list');
1.5 LLM Vector Storage (Embedding)
In AI applications, text is converted into vectors through an embedding model and stored in a vector database (such as Pinecone, Milvus) for semantic retrieval.
Difference from Redis: Redis stores exact key-value pairs, while vector databases store high-dimensional vectors and support similarity searches.
2. The Evolution of Form Submission: From Synchronous to Asynchronous
2.1 Traditional Form Submission (Page Refresh)
<form action="/submit" method="POST">
<input name="username" />
<button type="submit">Submit</button>
</form>
After clicking submit, the browser sends a request to the action address and refreshes the page. The user experience is poor because the page flickers and state is lost.
2.2 AJAX / Fetch: No-Refresh Submission
Use JavaScript to intercept the form submission event and send data asynchronously via fetch or XMLHttpRequest.
document.querySelector('form').addEventListener('submit', async (e) => {
e.preventDefault(); // Prevent default refresh behavior
const formData = new FormData(e.target);
const response = await fetch('/api/submit', {
method: 'POST',
body: formData,
});
const result = await response.json();
console.log('Submission successful', result);
});
Advantages: No page refresh, smooth user experience, allows partial UI updates.
3. A Complete Guide to this (Including 6 Scenarios)
this is the execution context dynamically bound at function runtime, unrelated to where the function is declared, only related to how it is called.
3.1 Normal Function Call (Non-Strict Mode)
function show() {
console.log(this); // Non-strict mode → window, Strict mode → undefined
}
show();
Enable strict mode to avoid polluting the global scope:
'use strict';
function show() {
console.log(this); // undefined
}
show();
3.2 Called as an Object Method
const obj = {
name: 'Jin Xiaoxuan',
say() {
console.log(this.name); // 'Jin Xiaoxuan'
}
};
obj.say();
3.3 Losing this via Reference Assignment
const obj = {
name: 'Jin Xiaoxuan',
say() {
console.log(this.name);
}
};
const fn = obj.say; // Reference assignment
fn(); // undefined (in non-strict mode, this points to window)
Solution: Use bind to bind this.
3.4 Called as a Constructor
function Person(name) {
this.name = name;
}
const p = new Person('Mingming');
console.log(p.name); // 'Mingming'
In a constructor, this points to the newly created instance object, and it automatically returns that object (unless another object is explicitly returned).
3.5 Event Handler Functions
document.querySelector('.link').addEventListener('click', function(e) {
console.log(this); // Points to the .link element
e.preventDefault();
});
Demonstrating the event bubbling and capturing phases, and the pointing of
thisin event handling
3.6 Manually Specifying this (call / apply / bind)
| Method | Execution Timing | Argument Passing Method |
|---|---|---|
call |
Executes immediately | Passed one by one |
apply |
Executes immediately | Passed as an array |
bind |
Returns a new function, does not execute immediately | Passed one by one (currying) |
const obj = { name: 'Mingming' };
function speak(a, b) {
console.log(this.name, a, b);
}
speak.call(obj, 'hello', 'world'); // Mingming hello world
speak.apply(obj, ['hello', 'world']); // Mingming hello world
const bound = speak.bind(obj, 'hello');
bound('world'); // Mingming hello world
3.7 Arrow Functions: No Own this
Arrow functions do not bind their own this; they inherit the this from the outer scope (Lexical Scope).
const obj = {
name: 'Mbappé',
say() {
console.log(this.name); // 'Mbappé' (this points to obj)
setTimeout(() => {
console.log(this.name); // 'Mbappé' (arrow function inherits outer this)
}, 1000);
}
};
obj.say();
If a normal function is used:
setTimeout(function() {
console.log(this.name); // undefined or window.name
}, 1000);
Conclusion: In scenarios where the outer this needs to be preserved (such as timers, event callbacks), arrow functions are a more elegant choice.
4. Comprehensive Practice: LocalStorage Todo Application
Below is a complete "Tapas List" example, combining LocalStorage storage, asynchronous form submission, and event handling with this binding.
4.1 HTML Structure
<!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>Your Tapas List</p>
<ul class="plates">
<li>Loading Tapas...</li>
</ul>
<form class="add-items">
<input type="text" name="item" placeholder="Add a Tapas" required>
<input type="submit" value="Add">
</form>
<a href="https://www.baidu.com" class="link">Go to Baidu</a>
</div>
<script src="./common.js?v=4"></script>
</body>
</html>
4.2 JavaScript Logic (common.js)
'use strict';
// 1. DOM References
const oForm = document.querySelector('.add-items');
const oList = document.querySelector('.plates');
// 2. Read data from localStorage
function loadItems() {
const data = localStorage.getItem('tapas');
return data ? JSON.parse(data) : [];
}
// 3. Render list
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. Save to localStorage
function saveItems(items) {
localStorage.setItem('tapas', JSON.stringify(items));
}
// 5. Add new item (using bind to bind this)
function AddItem(e) {
e.preventDefault(); // Prevent default form submission
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 = ''; // Clear input field
}
// 6. Bind event (using bind to manually specify this as the form element)
oForm.addEventListener('submit', AddItem.bind(oForm));
// 7. Handle checkbox changes (event delegation)
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. Initialize
render(loadItems());
// 9. Prevent Baidu link navigation (demonstrating e.preventDefault)
document.querySelector('.link').addEventListener('click', function(e) {
e.preventDefault();
console.log('Link intercepted, no navigation');
});
5. About Version Numbers and Cache Updates
When importing external resources, we often use ?v=version_number to force the browser to load the latest file and avoid caching old versions.
<script src="./common.js?v=4"></script>
After each modification to the JS, incrementing the version number allows users to get the latest code without manually clearing the cache.
6. Summary
| Knowledge Point | Core Points |
|---|---|
| Storage System | Browser Cache (fast) → LocalStorage (persistent) → Redis (ultra-fast KV) → Vector Database (AI) |
| Form Submission | Default submission refreshes the page → Use e.preventDefault() + fetch for no-refresh |
this Binding |
Call method determines this: method call → object; event → element; constructor → instance; call/apply/bind manually specify |
| Arrow Functions | No own this, inherits outer scope, suitable for callbacks and timers |
| LocalStorage | Stores key-value pairs, capacity ~5-10MB, suitable for small persistent data |
💡 Thought Question: If you need to synchronize data across multiple browser tabs, can localStorage do it? If not, what are the alternatives? (Hint: search for the
storageevent orBroadcastChannel)
I hope this article helps you build a complete cognitive system for frontend storage, form interaction, and this binding. If you find it useful, please like, bookmark, and share!