前端JavaScript徹底解決重復(fù)請(qǐng)求問(wèn)題的五種方案
引言
在前端開(kāi)發(fā)中,重復(fù)請(qǐng)求是一個(gè)常見(jiàn)且棘手的問(wèn)題。比如用戶快速點(diǎn)擊"保存"按鈕導(dǎo)致生成多條重復(fù)單據(jù),或者列表頁(yè)頻繁刷新造成服務(wù)器壓力飆升,這些場(chǎng)景不僅影響用戶體驗(yàn),還可能引發(fā)數(shù)據(jù)一致性問(wèn)題。本文將系統(tǒng)梳理重復(fù)請(qǐng)求的解決方案,從基礎(chǔ)到進(jìn)階進(jìn)行對(duì)比分析,并結(jié)合實(shí)際代碼案例解決這一痛點(diǎn)。
一、重復(fù)請(qǐng)求不止是"多花錢(qián)"
在討論解決方案前,我們先明確重復(fù)請(qǐng)求的具體影響,避免因"覺(jué)得問(wèn)題不大"而忽視它:
- 數(shù)據(jù)一致性風(fēng)險(xiǎn):如表單重復(fù)提交導(dǎo)致生成多個(gè)相同訂單、重復(fù)創(chuàng)建用戶,后續(xù)需要額外成本修復(fù)數(shù)據(jù)
- 服務(wù)器資源浪費(fèi):相同請(qǐng)求反復(fù)發(fā)送,占用帶寬和服務(wù)器算力,極端情況下可能引發(fā)服務(wù)過(guò)載
- 前端體驗(yàn)降級(jí):重復(fù)請(qǐng)求可能導(dǎo)致頁(yè)面多次渲染閃爍,或觸發(fā)多次錯(cuò)誤提示
- 網(wǎng)絡(luò)資源消耗:尤其在移動(dòng)端,重復(fù)請(qǐng)求會(huì)浪費(fèi)用戶流量,增加加載時(shí)間
了解危害后,我們來(lái)看當(dāng)前主流的解決方案,及其適用場(chǎng)景和優(yōu)缺點(diǎn)。
二、5種重復(fù)請(qǐng)求解決方案對(duì)比
方案1:UI層面控制(最簡(jiǎn)單但不徹底)
這是最基礎(chǔ)的解決方案,通過(guò)控制UI交互阻止重復(fù)觸發(fā)請(qǐng)求,核心思路是"讓用戶無(wú)法重復(fù)點(diǎn)擊"。
實(shí)現(xiàn)方式
- 按鈕點(diǎn)擊后立即禁用,直到請(qǐng)求完成(成功/失?。┖笾匦聠⒂?/li>
- 列表刷新時(shí)顯示加載狀態(tài),禁止再次觸發(fā)刷新操作
- 路由切換時(shí)取消當(dāng)前頁(yè)面未完成的請(qǐng)求
代碼示例(React)
const SaveButton = () => {
const [loading, setLoading] = useState(false);
const handleSave = async () => {
if (loading) return; // 防止重復(fù)觸發(fā)
setLoading(true);
try {
await api.submitForm(data);
message.success("保存成功");
} catch (error) {
message.error("保存失敗");
} finally {
setLoading(false); // 請(qǐng)求完成后恢復(fù)按鈕狀態(tài)
}
};
return <Button loading={loading} onClick={handleSave}>保存</Button>;
}; 優(yōu)缺點(diǎn)分析
實(shí)現(xiàn)簡(jiǎn)單,無(wú)額外依賴
對(duì)現(xiàn)有代碼侵入性低
即時(shí)反饋,提升用戶體驗(yàn)
無(wú)法覆蓋所有場(chǎng)景(如代碼層面直接調(diào)用接口)
多個(gè)組件調(diào)用同一接口時(shí),無(wú)法共享狀態(tài)
無(wú)法處理網(wǎng)絡(luò)延遲導(dǎo)致的"隱性重復(fù)請(qǐng)求"
適用場(chǎng)景
- 簡(jiǎn)單表單提交、單按鈕交互場(chǎng)景
- 快速迭代的小型項(xiàng)目,無(wú)復(fù)雜接口調(diào)用邏輯
方案2:請(qǐng)求攔截器+緩存(適合讀操作)
對(duì)于查詢類(lèi)接口(如列表查詢、詳情獲?。?,可通過(guò)"請(qǐng)求攔截器+緩存"實(shí)現(xiàn)重復(fù)請(qǐng)求攔截,核心思路是"相同請(qǐng)求只發(fā)一次,結(jié)果緩存復(fù)用"。
實(shí)現(xiàn)原理
- 定義緩存容器(如Map),存儲(chǔ)已發(fā)送但未完成的請(qǐng)求Promise
- 發(fā)起請(qǐng)求前,生成請(qǐng)求唯一標(biāo)識(shí)(如URL+參數(shù)+方法的哈希值)
- 若緩存中存在該請(qǐng)求的Promise,直接返回緩存的Promise;若不存在,發(fā)送請(qǐng)求并將Promise存入緩存
- 請(qǐng)求完成(成功/失?。┖螅宄彺?,確保下次請(qǐng)求可正常發(fā)起
代碼示例(Axios攔截器)
import axios from 'axios';
import { sha256 } from 'js-sha256';
// 緩存容器:key=請(qǐng)求唯一標(biāo)識(shí),value=請(qǐng)求Promise
const requestCache = new Map();
// 創(chuàng)建Axios實(shí)例
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 5000
});
// 請(qǐng)求攔截器
service.interceptors.request.use(
(config) => {
// 1. 生成請(qǐng)求唯一標(biāo)識(shí)(URL+方法+參數(shù))
const requestKey = generateRequestKey(config);
// 2. 檢查緩存:若存在未完成的請(qǐng)求,直接返回緩存的Promise
if (requestCache.has(requestKey)) {
return requestCache.get(requestKey);
}
// 3. 若不存在緩存,發(fā)送請(qǐng)求并緩存Promise
const requestPromise = Promise.resolve(config);
requestCache.set(requestKey, requestPromise);
return requestPromise;
},
(error) => Promise.reject(error)
);
// 響應(yīng)攔截器
service.interceptors.response.use(
(response) => {
// 請(qǐng)求完成,清除緩存
const requestKey = generateRequestKey(response.config);
requestCache.delete(requestKey);
return response.data;
},
(error) => {
// 請(qǐng)求失敗,同樣清除緩存(避免緩存失敗狀態(tài))
if (error.config) {
const requestKey = generateRequestKey(error.config);
requestCache.delete(requestKey);
}
return Promise.reject(error);
}
);
// 生成請(qǐng)求唯一標(biāo)識(shí):基于URL、方法、params、data的哈希值
function generateRequestKey(config) {
const { url, method, params, data } = config;
const requestStr = JSON.stringify({ url, method, params, data });
// 使用sha256生成哈希值,確保唯一性
return sha256(requestStr);
}
export default service; 優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
對(duì)業(yè)務(wù)代碼無(wú)侵入,全局生效
減少重復(fù)請(qǐng)求,減輕服務(wù)器壓力
支持多組件共享請(qǐng)求結(jié)果
缺點(diǎn):
不適合寫(xiě)操作(如新增/修改/刪除),可能導(dǎo)致數(shù)據(jù)更新不及時(shí)
緩存有效期難控制,需手動(dòng)處理過(guò)期邏輯
無(wú)法處理請(qǐng)求取消場(chǎng)景
適用場(chǎng)景
- 讀操作接口(如列表查詢、詳情獲取、下拉選單數(shù)據(jù)加載)
- 無(wú)實(shí)時(shí)數(shù)據(jù)要求的場(chǎng)景,允許短期緩存
方案3:請(qǐng)求取消+狀態(tài)管理(適合寫(xiě)操作)
對(duì)于寫(xiě)操作接口(如新增、修改、刪除),不能使用緩存(需確保每次請(qǐng)求都能觸達(dá)服務(wù)器),此時(shí)需通過(guò)"請(qǐng)求取消+狀態(tài)管理"實(shí)現(xiàn)重復(fù)攔截,核心思路是"相同寫(xiě)請(qǐng)求同時(shí)只能存在一個(gè),重復(fù)請(qǐng)求直接取消"。
實(shí)現(xiàn)原理
- 維護(hù)一個(gè)請(qǐng)求狀態(tài)容器,存儲(chǔ)當(dāng)前未完成的寫(xiě)請(qǐng)求標(biāo)識(shí)及對(duì)應(yīng)的取消函數(shù)
- 發(fā)起寫(xiě)請(qǐng)求前,生成請(qǐng)求唯一標(biāo)識(shí),檢查容器:若存在相同請(qǐng)求,調(diào)用取消函數(shù)取消新請(qǐng)求
- 若不存在相同請(qǐng)求,創(chuàng)建AbortController(或CancelToken),將取消函數(shù)和請(qǐng)求標(biāo)識(shí)存入容器
- 請(qǐng)求完成(成功/失?。┗蛉∠?,從容器中移除該請(qǐng)求標(biāo)識(shí)
代碼示例(結(jié)合AbortController)
import axios from 'axios';
import { sha256 } from 'js-sha256';
// 管理未完成的寫(xiě)請(qǐng)求:key=請(qǐng)求唯一標(biāo)識(shí),value=AbortController
const pendingWriteRequests = new Map();
// 寫(xiě)請(qǐng)求專(zhuān)用Axios實(shí)例
const writeService = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 5000
});
// 發(fā)起寫(xiě)請(qǐng)求(如POST/PUT/DELETE)
export function sendWriteRequest(config) {
// 1. 生成請(qǐng)求唯一標(biāo)識(shí)
const requestKey = generateRequestKey(config);
// 2. 檢查是否存在未完成的相同請(qǐng)求:若有,取消新請(qǐng)求
if (pendingWriteRequests.has(requestKey)) {
const newController = new AbortController();
// 取消新請(qǐng)求
newController.abort('重復(fù)請(qǐng)求已取消');
return Promise.reject(new Error('重復(fù)請(qǐng)求已取消'));
}
// 3. 創(chuàng)建AbortController,用于取消請(qǐng)求
const controller = new AbortController();
const newConfig = {
...config,
signal: controller.signal // 綁定取消信號(hào)
};
// 4. 將請(qǐng)求標(biāo)識(shí)和取消控制器存入容器
pendingWriteRequests.set(requestKey, controller);
// 5. 發(fā)送請(qǐng)求,完成后清除容器
return writeService(newConfig)
.then((response) => {
pendingWriteRequests.delete(requestKey);
return response.data;
})
.catch((error) => {
pendingWriteRequests.delete(requestKey);
// 過(guò)濾"主動(dòng)取消"的錯(cuò)誤,避免業(yè)務(wù)層處理
if (error.name === 'AbortError') {
console.log('請(qǐng)求已取消:', requestKey);
return Promise.reject(new Error('請(qǐng)求已取消'));
}
return Promise.reject(error);
});
}
// 生成請(qǐng)求唯一標(biāo)識(shí)(同方案2)
function generateRequestKey(config) {
const { url, method, params, data } = config;
const requestStr = JSON.stringify({ url, method, params, data });
return sha256(requestStr);
}
// 手動(dòng)取消指定請(qǐng)求(如頁(yè)面卸載時(shí))
export function cancelWriteRequest(config) {
const requestKey = generateRequestKey(config);
if (pendingWriteRequests.has(requestKey)) {
const controller = pendingWriteRequests.get(requestKey);
controller.abort('手動(dòng)取消請(qǐng)求');
pendingWriteRequests.delete(requestKey);
}
}
export default writeService; 優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
適合寫(xiě)操作,確保數(shù)據(jù)一致性
支持手動(dòng)取消(如頁(yè)面卸載)
避免重復(fù)寫(xiě)請(qǐng)求導(dǎo)致的數(shù)據(jù)問(wèn)題
缺點(diǎn):
實(shí)現(xiàn)較復(fù)雜,需手動(dòng)管理取消邏輯
對(duì)業(yè)務(wù)代碼有一定侵入性(需使用專(zhuān)用請(qǐng)求函數(shù))
無(wú)法復(fù)用請(qǐng)求結(jié)果,每次請(qǐng)求都需觸達(dá)服務(wù)器
適用場(chǎng)景
- 寫(xiě)操作接口(如表單提交、數(shù)據(jù)修改、刪除操作)
- 對(duì)數(shù)據(jù)一致性要求高的場(chǎng)景(如訂單創(chuàng)建、支付請(qǐng)求)
方案4:訂閱-發(fā)布模式(多訂閱者共享請(qǐng)求結(jié)果)
當(dāng)多個(gè)組件同時(shí)調(diào)用同一接口時(shí),可通過(guò)"訂閱-發(fā)布模式"實(shí)現(xiàn)"一次請(qǐng)求,多端復(fù)用",核心思路是"相同請(qǐng)求只發(fā)送一次,結(jié)果分發(fā)給所有訂閱者",這也是參考范文中采用的核心方案。
實(shí)現(xiàn)原理
- 維護(hù)一個(gè)請(qǐng)求狀態(tài)容器:key=請(qǐng)求唯一標(biāo)識(shí),value=訂閱者列表+請(qǐng)求Promise
- 組件發(fā)起請(qǐng)求時(shí),生成請(qǐng)求唯一標(biāo)識(shí),檢查容器:
- 若請(qǐng)求已存在(未完成):將當(dāng)前組件的回調(diào)函數(shù)加入訂閱者列表
- 若請(qǐng)求不存在:發(fā)送請(qǐng)求,將Promise存入容器,并添加當(dāng)前組件的訂閱者
- 請(qǐng)求完成后,遍歷訂閱者列表,將結(jié)果分發(fā)給所有訂閱者
- 訂閱者取消訂閱(如組件卸載)時(shí),從訂閱者列表中移除自身
代碼示例(基于參考范文封裝)
import axios from 'axios';
import { sha256 } from 'js-sha256';
class RequestSubscriber {
// 容器:key=請(qǐng)求唯一標(biāo)識(shí),value={ promise: 請(qǐng)求Promise, subscribers: 訂閱者列表 }
constructor() {
this.requestStore = new Map();
this.instance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 5000
});
}
// 發(fā)起請(qǐng)求(訂閱)
request(config) {
const requestKey = this.generateRequestKey(config);
const storeItem = this.requestStore.get(requestKey);
// 1. 若請(qǐng)求已存在,添加訂閱者
if (storeItem) {
return new Promise((resolve, reject) => {
storeItem.subscribers.push({ resolve, reject });
});
}
// 2. 若請(qǐng)求不存在,創(chuàng)建請(qǐng)求并訂閱
const subscribers = [];
const controller = new AbortController();
const newConfig = { ...config, signal: controller.signal };
// 創(chuàng)建請(qǐng)求Promise
const requestPromise = this.instance(newConfig)
.then((response) => {
// 請(qǐng)求成功,通知所有訂閱者
this.notifySubscribers(requestKey, 'resolve', response.data);
return response.data;
})
.catch((error) => {
// 請(qǐng)求失敗,通知所有訂閱者
this.notifySubscribers(requestKey, 'reject', error);
return Promise.reject(error);
})
.finally(() => {
// 請(qǐng)求完成,清除容器
this.requestStore.delete(requestKey);
});
// 存入容器
this.requestStore.set(requestKey, {
promise: requestPromise,
subscribers,
controller
});
// 返回當(dāng)前訂閱的Promise
return new Promise((resolve, reject) => {
subscribers.push({ resolve, reject });
});
}
// 通知所有訂閱者
notifySubscribers(requestKey, type, data) {
const storeItem = this.requestStore.get(requestKey);
if (!storeItem) return;
storeItem.subscribers.forEach((subscriber) => {
subscriber[type](data);
});
}
// 取消請(qǐng)求(如組件卸載)
cancelRequest(config) {
const requestKey = this.generateRequestKey(config);
const storeItem = this.requestStore.get(requestKey);
if (storeItem) {
// 取消請(qǐng)求
storeItem.controller.abort('請(qǐng)求已取消');
// 清除容器
this.requestStore.delete(requestKey);
}
}
// 生成請(qǐng)求唯一標(biāo)識(shí)
generateRequestKey(config) {
const { url, method, params, data } = config;
const requestStr = JSON.stringify({ url, method, params, data });
return sha256(requestStr).slice(0, 40); // 截取前40位,平衡唯一性和長(zhǎng)度
}
}
// 單例模式:確保全局只有一個(gè)實(shí)例
export const requestSubscriber = new RequestSubscriber(); 優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
多組件共享請(qǐng)求結(jié)果,減少請(qǐng)求次數(shù)
支持請(qǐng)求取消,避免內(nèi)存泄漏
兼顧讀操作和寫(xiě)操作(寫(xiě)操作可關(guān)閉共享)
缺點(diǎn):
實(shí)現(xiàn)復(fù)雜,需維護(hù)訂閱者列表和請(qǐng)求狀態(tài)
調(diào)試難度高,需跟蹤訂閱者和請(qǐng)求狀態(tài)
對(duì)新手不友好,需理解訂閱-發(fā)布模式
適用場(chǎng)景
- 多組件同時(shí)調(diào)用同一接口的場(chǎng)景(如多個(gè)組件需要同一批下拉選單數(shù)據(jù))
- 大型項(xiàng)目,需統(tǒng)一管理請(qǐng)求狀態(tài)和訂閱關(guān)系
方案5:后端配合攔截(最徹底的方案)
前端方案雖能解決大部分場(chǎng)景,但仍存在"極端情況漏洞"(如網(wǎng)絡(luò)延遲導(dǎo)致的請(qǐng)求繞過(guò)前端攔截),此時(shí)需后端配合,從源頭攔截重復(fù)請(qǐng)求,核心思路是"后端基于唯一標(biāo)識(shí)判斷是否為重復(fù)請(qǐng)求"。
實(shí)現(xiàn)原理
- 前端發(fā)起請(qǐng)求時(shí),生成一個(gè)唯一標(biāo)識(shí)(如UUID),存入請(qǐng)求頭(如
X-Request-ID) - 后端接收到請(qǐng)求后,檢查
X-Request-ID:
- 若Redis中不存在該ID:處理請(qǐng)求,并將ID存入Redis(設(shè)置過(guò)期時(shí)間,如5秒)
- 若Redis中已存在該ID:判定為重復(fù)請(qǐng)求,直接返回"重復(fù)請(qǐng)求"錯(cuò)誤
- 前端接收到"重復(fù)請(qǐng)求"錯(cuò)誤后,提示用戶或忽略該響應(yīng)
代碼示例(前后端配合)
前端部分:
import axios from 'axios';
import { v4 as uuidv4 } from 'uuid';
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 5000
});
// 請(qǐng)求攔截器:添加唯一請(qǐng)求ID
service.interceptors.request.use(
(config) => {
// 生成唯一請(qǐng)求ID(UUID)
const requestId = uuidv4();
// 存入請(qǐng)求頭
config.headers['X-Request-ID'] = requestId;
// 存入localStorage,用于后續(xù)重復(fù)請(qǐng)求判斷(可選)
localStorage.setItem(`request_${requestId}`, 'pending');
return config;
},
(error) => Promise.reject(error)
);
// 響應(yīng)攔截器:處理重復(fù)請(qǐng)求錯(cuò)誤
service.interceptors.response.use(
(response) => {
const requestId = response.config.headers['X-Request-ID'];
// 請(qǐng)求完成,刪除localStorage中的標(biāo)識(shí)
localStorage.removeItem(`request_${requestId}`);
return response.data;
},
(error) => {
if (error.response?.data?.code === 'DUPLICATE_REQUEST') {
// 后端返回重復(fù)請(qǐng)求錯(cuò)誤,提示用戶
message.warning('請(qǐng)勿重復(fù)操作');
const requestId = error.config.headers['X-Request-ID'];
localStorage.removeItem(`request_${requestId}`);
return Promise.reject(new Error('重復(fù)請(qǐng)求已攔截'));
}
return Promise.reject(error);
}
);
export default service;
``
**后端部分(Node.js + Redis)**:
``const express = require('express');
const redis = require('redis');
const { v4: uuidv4 } = require('uuid');
const app = express();
const redisClient = redis.createClient({
url: process.env.REDIS_URL
});
redisClient.connect();
// 重復(fù)請(qǐng)求攔截中間件
app.use(async (req, res, next) => {
const requestId = req.headers['x-request-id'];
if (!requestId) {
return res.status(400).json({ code: 'INVALID_REQUEST', message: '缺少請(qǐng)求ID' });
}
// 檢查Redis中是否存在該請(qǐng)求ID
const exists = await redisClient.exists(`request:${requestId}`);
if (exists) {
// 已存在,判定為重復(fù)請(qǐng)求
return res.status(400).json({ code: 'DUPLICATE_REQUEST', message: '重復(fù)請(qǐng)求已攔截' });
}
// 不存在,存入Redis(設(shè)置5秒過(guò)期,避免內(nèi)存泄漏)
await redisClient.setEx(`request:${requestId}`, 5, 'pending');
next();
});
// 業(yè)務(wù)接口
app.post('/api/submit-form', (req, res) => {
// 處理表單提交邏輯
res.json({ code: 'SUCCESS', message: '提交成功' });
});
app.listen(3000, () => {
console.log('Server running on port 3000');
}); 優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
從源頭攔截重復(fù)請(qǐng)求,最徹底
不受前端環(huán)境影響(如多標(biāo)簽頁(yè)、多設(shè)備)
支持分布式系統(tǒng),可跨服務(wù)判斷重復(fù)請(qǐng)求
缺點(diǎn):
需后端配合,增加后端開(kāi)發(fā)成本
依賴Redis等存儲(chǔ)服務(wù),增加部署復(fù)雜度
需處理請(qǐng)求ID的過(guò)期邏輯,避免存儲(chǔ)膨脹
適用場(chǎng)景
- 對(duì)數(shù)據(jù)一致性要求極高的場(chǎng)景(如支付、訂單創(chuàng)建)
- 大型分布式系統(tǒng),前端攔截?zé)o法覆蓋所有場(chǎng)景
三、推薦組合方案實(shí)現(xiàn)"徹底解決"
單一方案無(wú)法覆蓋所有場(chǎng)景,實(shí)際項(xiàng)目中建議采用"組合方案",兼顧性能、體驗(yàn)和數(shù)據(jù)一致性:
- 基礎(chǔ)層:方案1(UI控制)+ 方案2(緩存)
- 所有按鈕點(diǎn)擊添加loading狀態(tài),防止重復(fù)觸發(fā)
- 所有讀操作接口添加緩存,減少服務(wù)器壓力
- 核心層:方案3(請(qǐng)求取消)+ 方案4(訂閱-發(fā)布)
- 所有寫(xiě)操作接口添加請(qǐng)求取消邏輯,避免重復(fù)提交
- 多組件共享的接口使用訂閱-發(fā)布模式,提升性能
- 保障層:方案5(后端配合)
- 核心業(yè)務(wù)接口(如支付、訂單)添加后端重復(fù)攔截
- 前端傳遞唯一請(qǐng)求ID,后端基于Redis判斷重復(fù)
通過(guò)這種"三層防護(hù)",可徹底解決前端重復(fù)請(qǐng)求問(wèn)題,同時(shí)兼顧開(kāi)發(fā)效率和系統(tǒng)穩(wěn)定性。
以上就是前端JavaScript徹底解決重復(fù)請(qǐng)求問(wèn)題的五種方案的詳細(xì)內(nèi)容,更多關(guān)于JavaScript解決重復(fù)請(qǐng)求的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
js模仿php中strtotime()與date()函數(shù)實(shí)現(xiàn)方法
這篇文章主要介紹了js模仿php中strtotime()與date()函數(shù)實(shí)現(xiàn)方法,涉及javascript時(shí)間操作的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-08-08
js通過(guò)var定義全局變量與在window對(duì)象上直接定義屬性的區(qū)別說(shuō)明
這篇文章主要介紹了js通過(guò)var定義全局變量與在window對(duì)象上直接定義屬性的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
JavaScript的原型存在的安全問(wèn)題及解決辦法
JavaScript的原型很多人都知道也很好用,但是很多人在使用原型繼承中導(dǎo)致的安全問(wèn)題卻很少人知道,接下來(lái)我們就來(lái)好好了解一下,感興趣的小伙伴跟著小編一起來(lái)看看吧2023-08-08
JavaScript作用域全面總結(jié)(附詳細(xì)代碼)
JavaScript的作用域一直以來(lái)是前端開(kāi)發(fā)中比較難以理解的知識(shí)點(diǎn),下面這篇文章主要介紹了JavaScript作用域全面總結(jié)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-11-11
淺談javascript中的prototype和__proto__的理解
這篇文章主要介紹了淺談javascript中的prototype和__proto__的理解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04

