当前位置: 首页 > news >正文

JavaScript系列(75)--代理模式专题

JavaScript代理模式专题 🎭

JavaScript的Proxy提供了强大的对象代理能力,能够拦截和自定义对象的基本操作。本文将深入探讨Proxy的各种模式、应用场景和最佳实践。

代理基础 🌟

💡 小知识:代理模式允许我们创建一个对象的代理,从而可以控制对这个对象的访问。JavaScript的Proxy API提供了13种基本操作的拦截器(trap),使我们能够自定义对象的行为。

// 基础代理操作
const target = { name: '张三', age: 25 };
const handler = {
    get(target, property) {
        console.log(`访问属性: ${property}`);
        return target[property];
    },
    set(target, property, value) {
        console.log(`设置属性: ${property} = ${value}`);
        target[property] = value;
        return true;
    }
};

const proxy = new Proxy(target, handler);
console.log(proxy.name);  // 输出: 访问属性: name 然后是 "张三"
proxy.age = 26;          // 输出: 设置属性: age = 26

Proxy拦截器详解 📋

1. 基本拦截器

class BasicTraps {
    static demonstrateBasicTraps() {
        const handler = {
            // 属性读取拦截
            get(target, prop, receiver) {
                return Reflect.get(target, prop, receiver);
            },
            
            // 属性设置拦截
            set(target, prop, value, receiver) {
                return Reflect.set(target, prop, value, receiver);
            },
            
            // 属性删除拦截
            deleteProperty(target, prop) {
                return Reflect.deleteProperty(target, prop);
            },
            
            // 属性存在性检查拦截
            has(target, prop) {
                return Reflect.has(target, prop);
            }
        };
        
        return new Proxy({}, handler);
    }
}

2. 高级拦截器

class AdvancedTraps {
    static demonstrateAdvancedTraps() {
        const handler = {
            // 对象属性枚举拦截
            ownKeys(target) {
                return Reflect.ownKeys(target);
            },
            
            // 属性描述符获取拦截
            getOwnPropertyDescriptor(target, prop) {
                return Reflect.getOwnPropertyDescriptor(target, prop);
            },
            
            // 原型获取拦截
            getPrototypeOf(target) {
                return Reflect.getPrototypeOf(target);
            },
            
            // 原型设置拦截
            setPrototypeOf(target, proto) {
                return Reflect.setPrototypeOf(target, proto);
            }
        };
        
        return new Proxy({}, handler);
    }
}

3. 函数和构造器拦截

class FunctionTraps {
    static demonstrateFunctionTraps() {
        function target(a, b) {
            return a + b;
        }
        
        const handler = {
            // 函数调用拦截
            apply(target, thisArg, args) {
                console.log(`调用函数,参数:${args}`);
                return Reflect.apply(target, thisArg, args);
            },
            
            // 构造函数调用拦截
            construct(target, args, newTarget) {
                console.log(`构造函数调用,参数:${args}`);
                return Reflect.construct(target, args, newTarget);
            }
        };
        
        return new Proxy(target, handler);
    }
}

常用代理模式 💼

1. 验证代理

class ValidationProxy {
    static createValidator(validationRules) {
        return new Proxy({}, {
            set(target, property, value) {
                if (validationRules[property]) {
                    const [isValid, message] = validationRules[property](value);
                    if (!isValid) {
                        throw new Error(`验证失败: ${property} - ${message}`);
                    }
                }
                return Reflect.set(target, property, value);
            }
        });
    }
}

// 使用示例
const userValidator = ValidationProxy.createValidator({
    age: (value) => [
        Number.isInteger(value) && value >= 0 && value <= 150,
        '年龄必须是0-150之间的整数'
    ],
    email: (value) => [
        /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
        '邮箱格式不正确'
    ]
});

2. 日志代理

class LoggingProxy {
    static createLogger(target, logCallback = console.log) {
        return new Proxy(target, {
            get(target, property) {
                logCallback(`获取属性: ${property}`);
                return Reflect.get(target, property);
            },
            
            set(target, property, value) {
                logCallback(`设置属性: ${property} = ${value}`);
                return Reflect.set(target, property, value);
            },
            
            deleteProperty(target, property) {
                logCallback(`删除属性: ${property}`);
                return Reflect.deleteProperty(target, property);
            }
        });
    }
}

// 使用示例
const user = LoggingProxy.createLogger({
    name: '张三',
    age: 25
});

3. 访问控制代理

class AccessControlProxy {
    static createPrivateProperties(target, privateProps = []) {
        return new Proxy(target, {
            get(target, property) {
                if (privateProps.includes(property)) {
                    throw new Error(`无法访问私有属性: ${property}`);
                }
                return Reflect.get(target, property);
            },
            
            set(target, property, value) {
                if (privateProps.includes(property)) {
                    throw new Error(`无法修改私有属性: ${property}`);
                }
                return Reflect.set(target, property, value);
            },
            
            deleteProperty(target, property) {
                if (privateProps.includes(property)) {
                    throw new Error(`无法删除私有属性: ${property}`);
                }
                return Reflect.deleteProperty(target, property);
            }
        });
    }
}

4. 缓存代理

class CachingProxy {
    static createCached(target, ttl = 5000) {
        const cache = new Map();
        
        return new Proxy(target, {
            apply(target, thisArg, args) {
                const key = JSON.stringify(args);
                const now = Date.now();
                
                if (cache.has(key)) {
                    const [result, timestamp] = cache.get(key);
                    if (now - timestamp < ttl) {
                        return result;
                    }
                }
                
                const result = Reflect.apply(target, thisArg, args);
                cache.set(key, [result, now]);
                return result;
            }
        });
    }
}

// 使用示例
const expensiveOperation = CachingProxy.createCached(
    (x, y) => {
        console.log('执行计算...');
        return x + y;
    }
);

最佳实践 ⭐

  1. 结合Reflect API使用
// 推荐
const handler = {
    get(target, prop, receiver) {
        return Reflect.get(target, prop, receiver);
    }
};

// 不推荐
const handler = {
    get(target, prop) {
        return target[prop];
    }
};
  1. 合理使用代理链
function createProxyChain(...handlers) {
    return (target) => {
        return handlers.reduce((proxy, handler) => {
            return new Proxy(proxy, handler);
        }, target);
    };
}
  1. 错误处理
const handler = {
    get(target, prop, receiver) {
        try {
            return Reflect.get(target, prop, receiver);
        } catch (error) {
            console.error(`获取属性 ${prop} 失败:`, error);
            return undefined;
        }
    }
};

性能考虑 ⚡

  1. 避免过度代理
// 不推荐
function createProxy(obj) {
    return new Proxy(obj, {
        get: (target, prop) => Reflect.get(target, prop)  // 无意义的代理
    });
}

// 推荐
function createProxy(obj) {
    return obj;  // 如果不需要拦截,直接返回原对象
}
  1. 缓存代理结果
class ProxyCache {
    constructor() {
        this.cache = new WeakMap();
    }
    
    createProxy(target, handler) {
        if (this.cache.has(target)) {
            return this.cache.get(target);
        }
        
        const proxy = new Proxy(target, handler);
        this.cache.set(target, proxy);
        return proxy;
    }
}
  1. 合理使用可撤销代理
function createRevocableProxy(target, handler) {
    const { proxy, revoke } = Proxy.revocable(target, handler);
    
    // 在不需要时撤销代理
    setTimeout(() => {
        revoke();
    }, 5000);
    
    return proxy;
}

总结 📝

JavaScript的Proxy API提供了:

  1. 强大的对象操作拦截能力
  2. 灵活的代理模式实现方式
  3. 与Reflect API的完美配合
  4. 丰富的实际应用场景

💡 学习建议:

  • 深入理解各种代理拦截器
  • 掌握常用代理模式
  • 注意性能影响
  • 合理使用代理链
  • 始终做好错误处理

如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻

相关文章:

  • MySQL中count(1)和count(*) 的区别
  • docker的mysql容器修改数据库root的登录密码后,navicat依然能用旧密码访问
  • MySQL数据库入门到大蛇尚硅谷宋红康老师笔记 基础篇 part 18
  • 超详细|25保研时间线及规划
  • IIS asp.net权限不足
  • 从MySQL5.7平滑升级到MySQL8.0的最佳实践分享
  • Redis的持久化机制
  • 计算机考研之数据结构:深入解析最大公约数与欧几里得算法
  • .NET 9.0 的 Blazor Web App 项目中 EF Core 【事务】使用备忘
  • 第一章——1.1 Java程序设计平台
  • BIO、NIO 和 AIO 的区别?
  • k8s集群如何赋权普通用户仅管理指定命名空间资源
  • 快速排序_912. 排序数组(10中排序算法)
  • 基于 VScode 的 git 详细使用指南【保姆级!建议收藏!】
  • tcp连接的11种状态及常见问题。
  • Layui 列表中switch按钮的使用
  • 负载均衡 方式
  • 聚焦工控物联网网关
  • Vue3项目,蛋糕商城系统
  • 基于Django快递物流管理可视化分析系统(完整系统源码+数据库+详细开发文档+万字详细论文+答辩PPT+详细部署教程等资料)
  • 无人机穿越大理崇圣寺千年古塔时“炸机”,当地:肇事者已找到,将被追责
  • 马上评|独生子女奖励不能“私了”,政府诚信是第一诚信
  • 丁薛祥在学习《习近平经济文选》第一卷专题研讨班上强调:深入学习贯彻习近平经济思想,加强党中央对经济工作的集中统一领导
  • 郭旭涛转任河北省科协党组书记、常务副主席,曾任团省委书记
  • 100%关税!特朗普要让美国电影100%美国制造
  • 巴菲特股东大会精华版:批评拿贸易当武器,宣布年底交班