代理模式的優缺點是什么?

什么是代理模式?

代理模式(Proxy Pattern)是一種結構型設計模式,它通過創建代理對象來控制對原始對象的訪問。

這種模式在前端開發中廣泛應用,特別是在需要控制對象訪問、添加額外邏輯或優化性能的場景中。

??核心思想??:在客戶端代碼和真實對象之間添加中間層,這個中間層(代理)可以:

  1. 控制對原始對象的訪問權限
  2. 添加預處理/后處理邏輯
  3. 實現延遲加載
  4. 緩存昂貴操作的結果
  5. 記錄日志或監控行為

代理模式實現方式

1. 虛擬代理(延遲加載)

// 原始圖片加載器
class ImageLoader {constructor(url) {this.url = url;}load() {console.log(`Loading image from ${this.url}`);return `<img src="${this.url}" />`;}
}// 虛擬代理 - 延遲加載
class ImageProxy {constructor(url) {this.url = url;this.imageLoader = null; // 延遲初始化}load() {if (!this.imageLoader) {this.imageLoader = new ImageLoader(this.url);// 添加占位邏輯const placeholder = document.createElement('div');placeholder.style.width = '300px';placeholder.style.height = '200px';placeholder.style.background = '#eee';document.body.appendChild(placeholder);// 延遲實際加載setTimeout(() => {document.body.removeChild(placeholder);document.body.innerHTML += this.imageLoader.load();}, 2000);}return 'Image loading initiated...';}
}// 使用代理
const image = new ImageProxy('https://example.com/large-image.jpg');
console.log(image.load()); // 立即返回占位符,2秒后加載真實圖片

2. 緩存代理(記憶函數)

// 原始計算函數
function fibonacci(n) {if (n <= 1) return n;return fibonacci(n - 1) + fibonacci(n - 2);
}// 緩存代理
function createCachedProxy(fn) {const cache = new Map();return function(n) {if (cache.has(n)) {console.log(`Cache hit for n=${n}`);return cache.get(n);}const result = fn(n);cache.set(n, result);console.log(`Calculated for n=${n}`);return result;};
}// 使用代理
const cachedFib = createCachedProxy(fibonacci);console.log(cachedFib(35)); // 長時間計算
console.log(cachedFib(35)); // 立即返回緩存結果

3. 保護代理(訪問控制)

// 原始用戶服務
class UserService {constructor() {this.users = new Map([[1, { id: 1, name: 'Admin', role: 'admin' }]]);}deleteUser(id) {this.users.delete(id);return `User ${id} deleted`;}
}// 保護代理
class UserServiceProxy {constructor(user) {this.userService = new UserService();this.currentUser = user;}deleteUser(id) {if (this.currentUser.role !== 'admin') {throw new Error('Permission denied');}return this.userService.deleteUser(id);}
}// 使用代理
const adminProxy = new UserServiceProxy({ role: 'admin' });
console.log(adminProxy.deleteUser(1)); // 成功const userProxy = new UserServiceProxy({ role: 'user' });
userProxy.deleteUser(1); // 拋出權限錯誤

4. ES6 Proxy實現

// 原始對象
const database = {users: {1: { name: 'Alice', email: 'alice@example.com' }},getEmail: function(userId) {return this.users[userId]?.email;}
};// 創建代理
const protectedDatabase = new Proxy(database, {get(target, prop) {// 權限驗證if (prop === 'users') {throw new Error('Direct access to users denied');}return target[prop];},set(target, prop, value) {// 寫操作限制if (prop === 'users') {throw new Error('User modification requires admin privileges');}target[prop] = value;return true;}
});console.log(protectedDatabase.getEmail(1)); // 正常訪問
protectedDatabase.users = {}; // 拋出錯誤
console.log(protectedDatabase.users); // 拋出錯誤

代理模式優缺點分析

優點:

  1. ??訪問控制??:實現精細的權限管理
// API請求代理示例
class ApiProxy {constructor(api) {this.api = api;}async request(endpoint) {if (isRateLimited(endpoint)) {throw new Error('API rate limit exceeded');}trackRequest(endpoint);return this.api.request(endpoint);}
}
  1. ??性能優化??:通過緩存和延遲加載提升性能
// 圖片懶加載代理
const lazyImage = new Proxy(new Image(), {set(target, prop, value) {if (prop === 'src') {// 延遲到元素可見時加載const observer = new IntersectionObserver((entries) => {entries.forEach(entry => {if (entry.isIntersecting) {target.src = value;observer.unobserve(target);}});});observer.observe(target);return true;}return Reflect.set(...arguments);}
});document.body.appendChild(lazyImage);
lazyImage.src = 'https://example.com/large-image.jpg'; // 實際加載延遲到圖片可見時
  1. ??職責分離??:保持核心邏輯的純凈性
// 原始表單驗證
class FormValidator {validateEmail(email) {return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);}
}// 驗證代理添加日志
class LoggingValidatorProxy {constructor(validator) {this.validator = validator;}validateEmail(email) {const result = this.validator.validateEmail(email);console.log(`Email validation result for ${email}: ${result}`);return result;}
}

缺點:

  1. ??復雜性增加??:可能引入額外抽象層
// 過度設計的代理示例(不推薦)
class OverEngineeredProxy {constructor(service) {this.service = service;this.logger = new Logger();this.cache = new Cache();this.validator = new Validator();}async getData() {this.logger.logStart();if (!this.validator.validate()) {throw new Error('Validation failed');}const data = await this.cache.get('data') || this.service.getData();this.logger.logEnd();return data;}
}
  1. ??性能損耗??:額外的代理調用開銷
// 性能敏感的原始類
class Vector {constructor(x, y) {this.x = x;this.y = y;}add(other) {return new Vector(this.x + other.x, this.y + other.y);}
}// 添加日志代理可能影響性能
const loggedVector = new Proxy(new Vector(1,2), {get(target, prop) {if (typeof target[prop] === 'function') {return function(...args) {console.log(`Calling ${prop} with`, args);return target[prop].apply(target, args);};}return target[prop];}
});// 在需要高性能計算的場景中,這種代理會產生明顯開銷
  1. ??調試困難??:調用堆棧變深
// 多層代理導致的調試問題
const original = { method() { console.log('Original method'); } 
};const proxy1 = new Proxy(original, {get(target, prop) {console.log('Proxy1 handler');return target[prop];}
});const proxy2 = new Proxy(proxy1, {get(target, prop) {console.log('Proxy2 handler');return target[prop];}
});proxy2.method(); // 調用鏈:proxy2 -> proxy1 -> original

工程實踐建議

1. 表單驗證代理

// 原始表單對象
const form = {values: {},errors: {},submit() {console.log('Submitting:', this.values);}
};// 驗證代理
const validatedForm = new Proxy(form, {set(target, prop, value) {if (prop === 'values') {// 自動觸發驗證target.errors = validateForm(value);if (Object.keys(target.errors).length === 0) {target.submit();}}return Reflect.set(...arguments);}
});function validateForm(values) {const errors = {};if (!values.email?.includes('@')) errors.email = 'Invalid email';if (values.password?.length < 6) errors.password = 'Password too short';return errors;
}// 使用
validatedForm.values = { email: 'user@example.com', password: '12345' 
}; // 自動觸發驗證并顯示錯誤

2. API請求代理

// 請求代理工廠
function createApiProxy(api, config = {}) {return new Proxy(api, {get(target, prop) {const originalMethod = target[prop];if (typeof originalMethod !== 'function') return originalMethod;return async function(...args) {// 請求攔截if (config.beforeRequest) {args = config.beforeRequest(...args) || args;}try {const response = await originalMethod.apply(target, args);// 響應攔截return config.afterResponse ? config.afterResponse(response) : response;} catch (error) {// 錯誤處理if (config.errorHandler) {return config.errorHandler(error);}throw error;}};}});
}// 使用示例
const rawApi = {async getUser(id) {const res = await fetch(`/api/users/${id}`);return res.json();}
};const enhancedApi = createApiProxy(rawApi, {beforeRequest: (id) => {console.log(`Requesting user ${id}`);return [id]; // 可以修改參數},afterResponse: (data) => {console.log('Received response');return { ...data, fullName: `${data.firstName} ${data.lastName}` };},errorHandler: (error) => {console.error('API Error:', error);return { error: true, message: error.message };}
});// 調用方式保持一致
enhancedApi.getUser(123).then(console.log);

注意事項

  1. ??接口一致性??:代理必須保持與原對象相同的接口
// 錯誤的代理實現(接口不一致)
class BadProxy {constructor(file) {this.file = file;}// 遺漏了原始對象的save方法read() {return this.file.read();}
}
  1. ??避免深層代理嵌套??
// 不推薦的深層嵌套
const proxy1 = new Proxy(obj, handler1);
const proxy2 = new Proxy(proxy1, handler2);
const proxy3 = new Proxy(proxy2, handler3);
// 應盡量保持代理層級扁平
  1. ??注意內存管理??
// 代理導致的閉包內存泄漏
function createLeakyProxy() {const hugeData = new Array(1000000).fill('data');return new Proxy({}, {get(target, prop) {// 無意中持有hugeData的引用return hugeData[prop];}});
}
  1. ??性能關鍵路徑慎用??
// 在動畫循環中避免使用復雜代理
function animate() {// 直接訪問對象屬性element.x += velocity.x;element.y += velocity.y;// 而不是:// proxiedElement.x += velocity.x;requestAnimationFrame(animate);
}
  1. ??與裝飾器模式區分??
// 裝飾器模式(增強功能)
function withLogging(fn) {return function(...args) {console.log('Calling function');return fn(...args);};
}// 代理模式(控制訪問)
const proxiedFn = new Proxy(fn, {apply(target, thisArg, args) {if (!validate(args)) throw new Error('Invalid arguments');return Reflect.apply(target, thisArg, args);}
});

代理模式是前端架構中的重要模式,適用于:

  • 需要訪問控制的場景(權限驗證、流量控制)
  • 性能優化需求(緩存、延遲加載)
  • 增強監控能力(日志記錄、性能跟蹤)
  • 實現智能引用(自動清理、加載)

在實際工程中建議:

  1. 優先使用ES6 Proxy實現簡單代理邏輯
  2. 對性能敏感模塊謹慎使用
  3. 保持代理接口與原始對象一致
  4. 使用TypeScript增強類型安全
  5. 配合工廠模式創建復雜代理

正確使用代理模式可以提升代碼的可維護性和擴展性,但需要警惕模式濫用帶來的復雜性。

建議結合具體需求場景,在代碼清晰度和功能需求之間找到平衡點。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/75921.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/75921.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/75921.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【嵌入式學習3】UDP發送端、接收端

目錄 1、發送端 2、接收端 3、UDP廣播 1、發送端 from socket import *udp_socket socket(AF_INET,SOCK_DGRAM) udp_socket.bind(("127.0.0.1",3333))data_str "UDP發送端數據" data_bytes data_str.encode("utf-8") udp_socket.sendto(d…

AI重構SEO關鍵詞精準布局

內容概要 在傳統SEO策略面臨搜索場景碎片化、用戶意圖復雜化的挑戰下&#xff0c;AI技術通過多維數據分析與算法建模&#xff0c;正在重構關鍵詞布局的邏輯框架。基于自然語言處理&#xff08;NLP&#xff09;的語義分析能力&#xff0c;AI可精準識別搜索詞背后的需求層級&…

谷歌發布網絡安全AI新模型Sec-Gemini v1

谷歌近日宣布推出實驗性AI模型Sec-Gemini v1&#xff0c;旨在通過人工智能技術革新網絡安全防御體系。該模型由Sec-Gemini團隊成員Elie Burzstein和Marianna Tishchenko共同研發&#xff0c;旨在幫助網絡安全人員應對日益復雜的網絡威脅。 攻防不對稱的破局之道 Sec-Gemini團隊…

IntelliJ IDEA下開發FPGA——FPGA開發體驗提升__下

前言 由于Quartus寫代碼比較費勁&#xff0c;雖然新版已經有了代碼補全&#xff0c;但體驗上還有所欠缺。于是使用VS Code開發&#xff0c;效果如下所示&#xff0c;代碼樣式和基本的代碼補全已經可以滿足開發&#xff0c;其余工作則交由Quartus完成 但VS Code的自帶的git功能&…

Python語言的需求分析

Python語言的需求分析 引言 在信息技術快速發展的今天&#xff0c;編程語言的選擇對于軟件開發的成功與否起著至關重要的作用。Python作為一種高級編程語言&#xff0c;以其簡潔易讀的語法和強大的功能受到越來越多開發者的青睞。通過對Python語言的需求分析&#xff0c;我們…

抓wifi無線空口包之Ubuntu抓包(二)

一、設置網卡信道和頻段&#xff0c;并抓包 1、使用iwconfig查看自己機器的無線網卡名稱 wangwang-ThinkCentre-M930t-N000:~$ iwconfig lo no wireless extensions. eno1 no wireless extensions. enxc8a3624ab329 no wireless extensions. wlx90de80d1b5b1 IE…

深度學習實戰電力設備缺陷檢測

本文采用YOLOv11作為核心算法框架&#xff0c;結合PyQt5構建用戶界面&#xff0c;使用Python3進行開發。YOLOv11以其高效的實時檢測能力&#xff0c;在多個目標檢測任務中展現出卓越性能。本研究針對電力設備缺陷數據集進行訓練和優化&#xff0c;該數據集包含豐富的電力設備缺…

Day1:前端項目uni-app壁紙實戰

uni-app官網下載HBuilder。 uni-app快速上手 | uni-app官網 點擊HBuilder 安裝 新建項目 工具——插件安裝 安裝uni-app&#xff08;vue3&#xff09; 我們先來準備一下&#xff1a; 先在wallpaper下新建目錄 我已經建過了 同樣&#xff0c;再在common下建images和style目錄&…

mac命令操作

mac命令操作 快速刪除一行&#xff1a; control u 剪切文件&#xff1a;步驟1、先進行Command c 進行選擇復制文件&#xff0c;2、進行commandoptionv進行移動文件&#xff0c;如果commandv是進行復制文件。 commandcontrolD 三個鍵即可屏幕取詞進行翻譯 mac中可以使用快捷方…

C#語言的加密貨幣

C#語言與加密貨幣&#xff1a;一個開發者的探索之旅 引言 隨著區塊鏈技術的迅猛發展&#xff0c;加密貨幣的應用已經滲透到我們生活的方方面面。作為一種新興的數字資產&#xff0c;加密貨幣不僅改變了傳統的投資方式&#xff0c;而且對全球經濟產生了深遠影響。其中&#xf…

Mysql 概念

MySQL 是一種 關系型數據庫管理系統&#xff08;RDBMS&#xff09;&#xff0c;它使用 結構化查詢語言&#xff08;SQL&#xff09; 來管理和操作數據。它最初由瑞典公司 MySQL AB 開發&#xff0c;后來被 Sun Microsystems 收購&#xff0c;現在屬于 Oracle 公司。 一、MySQL…

Golang 項目平滑重啟

引言 平滑重啟&#xff08;Graceful Restart&#xff09;技術作為一種常用的解決方案&#xff0c;通過允許新進程接管而不中斷現有的請求&#xff0c;確保了系統的穩定運行和業務連續性。同時目前公司的服務重啟絕大部分也都適用的 go 的平滑重啟技術。 本部分將對平滑重啟的…

SQL SELECT DISTINCT 語句詳解:精準去重的藝術

在數據驅動的時代&#xff0c;數據質量直接影響決策的準確性。面對海量數據時&#xff0c;重復記錄如同沙礫中的金屑&#xff0c;既占用存儲空間&#xff0c;又干擾分析結果。SELECT DISTINCT 語句便是那把高效的篩子&#xff0c;助您快速剔除冗余&#xff0c;提取唯一值。本文…

16-產品經理-需求的評審

在創建需求的時候&#xff0c;有一個"不需要評審"的復選框&#xff0c;如果選中該復選框的話&#xff0c;需求的創建成功后狀態是激活的。 但大部分情況下面&#xff0c;需求還是需要評審的。 即使產品完全由一個人負責&#xff0c;也可以將一些不成熟的想法存為草…

計算機網絡學習前言

前言 該部分說明計算機網絡是什么&#xff1f;它有什么作用和功能&#xff1f;值不值得我們去學習&#xff1f;我們該如何學習&#xff1f;這幾個部分去大概介紹計算機網絡這門課程&#xff0c;往后會介紹計算機網絡的具體知識點。 1.計算機網絡是什么&#xff1f; 計算機網…

python全棧-JavaScript

python全棧-js 文章目錄 js基礎變量與常量JavaScript引入到HTML文件中JavaScript注釋與常見輸出方式 數據類型typeof 顯示數據類型算數運算符之加法運算符運算符之算術運算符運算符之賦值運算符運算符之比較運算符運算符之布爾運算符運算符之位運算符運算符優先級類型轉換 控制…

C語言一個偶數能表示為兩個素數之和

我們可以先找到其中的一個素數&#xff0c;然后用這個偶數減去這個素數就可以求得了。 運行結果:

vue實現大轉盤抽獎

用vue實現一個簡單的大轉盤抽獎案例 大轉盤 一 轉盤布局 <div class"lucky-wheel-content"><div class"lucky-wheel-prize" :style"wheelStyle" :class"isStart ? animated-icon : "transitionend"onWheelTransitionE…

Docker 核心組件

一、前言 Docker 已成為現代 DevOps 和微服務架構中的核心工具。為了更深入地理解它的工作機制&#xff0c;本文將系統介紹 Docker 的核心組件&#xff0c;配合結構圖直觀展示架構&#xff0c;同時拓展高級用法&#xff0c;幫助讀者全面掌握容器化技術的內核。 二、Docker 核心…

ModuleNotFoundError: No module named ‘pandas‘

在使用Python繪制散點圖表的時候&#xff0c;運行程序報錯&#xff0c;如圖&#xff1a; 報錯顯示Python 環境中可能沒有安裝 pandas 庫&#xff0c;執行pip list命令查看&#xff0c;果然沒有安裝pandas 庫&#xff0c;如圖&#xff1a; 執行命令&#xff1a;python -m pip in…