window.Proxy 是 JavaScript 的元编程特性,用于创建对象的代理(拦截器)! 🎯

🎯 基本概念

// Proxy 允许你创建一个对象的代理
// 可以拦截和自定义对象的基本操作
const target = {}; // 目标对象
const handler = {}; // 拦截器对象
const proxy = new Proxy(target, handler);

🚀 基本语法

// 创建 Proxy
const proxy = new Proxy(target, handler);

// target: 要代理的目标对象
// handler: 包含拦截方法的对象(陷阱)

🔧 核心拦截器(陷阱)

1. 基本操作拦截

const handler = {
    // 拦截属性读取
    get(target, property, receiver) {
        console.log(`读取属性: ${property}`);
        return target[property] || '默认值';
    },

    // 拦截属性设置
    set(target, property, value, receiver) {
        console.log(`设置属性: ${property} = ${value}`);
        target[property] = value;
        return true; // 表示成功
    },

    // 拦截属性删除
    deleteProperty(target, property) {
        console.log(`删除属性: ${property}`);
        delete target[property];
        return true;
    }
};

const obj = { name: '张三' };
const proxy = new Proxy(obj, handler);

proxy.name;        // 输出: 读取属性: name
proxy.age = 25;    // 输出: 设置属性: age = 25
delete proxy.name; // 输出: 删除属性: name

2. 存在性检查拦截

const handler = {
    // 拦截 in 操作符
    has(target, property) {
        console.log(`检查属性是否存在: ${property}`);
        return property in target;
    }
};

const obj = { name: '李四' };
const proxy = new Proxy(obj, handler);

console.log('name' in proxy); // 输出: 检查属性是否存在: name

🎯 实际应用场景

1. 数据验证

// 创建带验证的 Proxy
const validator = {
    set(target, property, value) {
        // 年龄验证
        if (property === 'age') {
            if (typeof value !== 'number' || value < 0 || value > 150) {
                throw new Error('年龄必须是 0-150 的数字');
            }
        }

        // 邮箱验证
        if (property === 'email') {
            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
            if (!emailRegex.test(value)) {
                throw new Error('邮箱格式不正确');
            }
        }

        target[property] = value;
        return true;
    }
};

const user = new Proxy({}, validator);

user.age = 25;    // ✅ 成功
user.age = 200;   // ❌ 抛出错误
user.email = 'test@example.com'; // ✅ 成功

2. 默认值和计算属性

const withDefaults = {
    get(target, property) {
        if (property in target) {
            return target[property];
        }

        // 提供默认值
        const defaults = {
            theme: 'light',
            language: 'zh-CN',
            pageSize: 10
        };

        return defaults[property] || `属性 ${property} 不存在`;
    }
};

const config = new Proxy({}, withDefaults);

console.log(config.theme);     // "light"(默认值)
console.log(config.language);  // "zh-CN"(默认值)
config.theme = 'dark';         // 设置自定义值
console.log(config.theme);     // "dark"(自定义值)

3. 观察者模式(数据绑定)

function createObservable(obj, onChange) {
    return new Proxy(obj, {
        set(target, property, value) {
            const oldValue = target[property];
            target[property] = value;

            // 触发变更回调
            if (oldValue !== value) {
                onChange(property, value, oldValue);
            }
            return true;
        }
    });
}

// 使用示例
const state = createObservable(
    { count: 0, name: '' },
    (key, newValue, oldValue) => {
        console.log(`状态变更: ${key}${oldValue} 变为 ${newValue}`);
    }
);

state.count = 1; // 输出: 状态变更: count 从 0 变为 1
state.name = '张三'; // 输出: 状态变更: name 从  变为 张三

4. 数组操作拦截

// 监听数组变化
const observableArray = (array, onChange) => {
    return new Proxy(array, {
        get(target, property) {
            const value = target[property];

            // 拦截数组方法
            if (typeof value === 'function') {
                return function(...args) {
                    const result = value.apply(target, args);
                    onChange(property, args, target);
                    return result;
                };
            }
            return value;
        }
    });
};

const arr = observableArray([1, 2, 3], (method, args, array) => {
    console.log(`数组操作: ${method}`, args, '=>', array);
});

arr.push(4);    // 输出: 数组操作: push [4] => [1,2,3,4]
arr.pop();      // 输出: 数组操作: pop [] => [1,2,3]

🔧 高级用法

1. 函数代理

// 代理函数调用
function logger(target) {
    return new Proxy(target, {
        apply(fn, thisArg, argumentsList) {
            console.log(`调用函数: ${fn.name}`, argumentsList);
            const result = fn.apply(thisArg, argumentsList);
            console.log(`返回结果:`, result);
            return result;
        }
    });
}

function add(a, b) {
    return a + b;
}

const loggedAdd = logger(add);
loggedAdd(2, 3); 
// 输出: 调用函数: add [2, 3]
// 输出: 返回结果: 5

2. 链式操作代理

function createChainable() {
    const methods = {
        select() { /* ... */ },
        where() { /* ... */ },
        orderBy() { /* ... */ }
    };

    return new Proxy({}, {
        get(target, property) {
            if (property in methods) {
                return function(...args) {
                    console.log(`调用方法: ${property}`, args);
                    // 返回代理自身,支持链式调用
                    return this;
                }.bind(proxy);
            }
            return target[property];
        }
    });
}

const query = createChainable();
query.select('name').where('age > 18').orderBy('name');

🎯 Vue 3 的响应式系统

Vue 3 使用 Proxy 实现响应式

// Vue 3 响应式原理(简化版)
function reactive(obj) {
    return new Proxy(obj, {
        get(target, key) {
            track(target, key); // 依赖收集
            return target[key];
        },
        set(target, key, value) {
            target[key] = value;
            trigger(target, key); // 触发更新
            return true;
        }
    });
}

// 使用示例
const state = reactive({ count: 0 });

// 自动响应式
console.log(state.count); // 触发 get
state.count = 1;         // 触发 set

⚠️ 注意事项

1. 目标对象保护

const obj = { data: '敏感信息' };
const proxy = new Proxy(obj, {
    get(target, property) {
        if (property === 'data') {
            return '访问被拒绝';
        }
        return target[property];
    }
});

console.log(proxy.data); // "访问被拒绝"

// ❌ 但仍然可以直接访问原对象
console.log(obj.data);   // "敏感信息"

2. this 绑定问题

const obj = {
    name: 'obj',
    getName() {
        return this.name;
    }
};

const proxy = new Proxy(obj, {
    get(target, property, receiver) {
        // receiver 确保正确的 this 绑定
        const value = target[property];
        if (typeof value === 'function') {
            return value.bind(receiver);
        }
        return value;
    }
});

console.log(proxy.getName()); // "obj"(正确绑定)

🔧 浏览器兼容性

// 检查浏览器支持
if (typeof Proxy !== 'undefined') {
    console.log('✅ 浏览器支持 Proxy');
} else {
    console.log('❌ 浏览器不支持 Proxy');
}

// 支持情况:
// Chrome 49+, Firefox 18+, Safari 10+, Edge 12+, Node.js 6+

🚀 与 Object.defineProperty 对比

Proxy 的优势

// 1. 拦截更多操作
const proxy = new Proxy(obj, {
    // 可以拦截这些操作:
    get, set, deleteProperty, has, apply, construct,
    getPrototypeOf, setPrototypeOf, isExtensible,
    preventExtensions, getOwnPropertyDescriptor,
    defineProperty, ownKeys
});

// 2. 数组响应式(Vue 2 的痛点)
const arr = new Proxy([1, 2, 3], {
    set(target, index, value) {
        console.log('数组变化:', index, value);
        target[index] = value;
        return true;
    }
});

arr.push(4); // 可以被拦截
arr[0] = 10; // 可以被拦截

总结

Proxy 的核心特性:

特性 说明
🎯 元编程 拦截和自定义对象基本操作
🚀 强大拦截 支持 13 种陷阱方法
📱 数组友好 完美处理数组变化检测
⚡ 性能优秀 比 defineProperty 更高效
🌍 现代标准 ES6 标准,主流浏览器支持

适用场景:

  • 响应式框架(Vue 3、MobX)
  • 数据验证
  • 观察者模式
  • API 封装
  • 权限控制

Proxy 是现代 JavaScript 元编程的强大工具! 💪