JavaScript动态代理的各种用法详解

 更新时间:2025年09月04日 08:41:20   作者:风冷  
动态代理是ES6引入的强大功能,通过Proxy对象实现,允许你拦截和自定义对目标对象的操作,下面我将全面介绍JavaScript动态代理的各种用法,需要的朋友可以参考下

引言

动态代理是ES6引入的强大功能,通过Proxy对象实现,允许你拦截和自定义对目标对象的操作。下面我将全面介绍JavaScript动态代理的各种用法,包括空对象、get方法拦截等高级特性。

1. Proxy基础

Proxy对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。

const target = {};
const handler = {
  get: function(target, prop) {
    return prop in target ? target[prop] : 37;
  }
};

const proxy = new Proxy(target, handler);
proxy.a = 1;
console.log(proxy.a); // 1
console.log(proxy.b); // 37 (handler中定义的默认值)

2. 空对象代理

创建一个"空对象"代理,可以拦截所有操作并返回默认值:

const emptyObject = new Proxy({}, {
  get(target, prop) {
    return undefined; // 所有属性访问返回undefined
  },
  set(target, prop, value) {
    return false; // 阻止所有赋值操作
  },
  has(target, prop) {
    return false; // 所有in操作返回false
  }
});

console.log(emptyObject.anyProperty); // undefined
emptyObject.a = 1; // 静默失败
console.log('a' in emptyObject); // false

3. Get方法拦截

get拦截器可以自定义属性访问行为:

const person = {
  name: 'John',
  age: 30
};

const proxy = new Proxy(person, {
  get(target, prop) {
    if (prop === 'age') {
      return `Age is ${target[prop]}`;
    }
    return target[prop] || `Property "${prop}" does not exist`;
  }
});

console.log(proxy.name); // "John"
console.log(proxy.age); // "Age is 30"
console.log(proxy.job); // "Property "job" does not exist"

4. 完整的拦截器方法

Proxy支持拦截多种操作:

const handler = {
  // 拦截属性读取
  get(target, prop, receiver) {
    console.log(`Getting ${prop}`);
    return Reflect.get(...arguments);
  },
  
  // 拦截属性设置
  set(target, prop, value, receiver) {
    console.log(`Setting ${prop} to ${value}`);
    return Reflect.set(...arguments);
  },
  
  // 拦截in操作符
  has(target, prop) {
    console.log(`Checking if ${prop} exists`);
    return Reflect.has(...arguments);
  },
  
  // 拦截delete操作
  deleteProperty(target, prop) {
    console.log(`Deleting ${prop}`);
    return Reflect.deleteProperty(...arguments);
  },
  
  // 拦截Object.keys等操作
  ownKeys(target) {
    console.log('Getting own keys');
    return Reflect.ownKeys(...arguments);
  },
  
  // 拦截函数调用(当代理目标是函数时)
  apply(target, thisArg, argumentsList) {
    console.log('Function called with', argumentsList);
    return Reflect.apply(...arguments);
  },
  
  // 拦截new操作符
  construct(target, argumentsList, newTarget) {
    console.log('Constructor called with', argumentsList);
    return Reflect.construct(...arguments);
  }
};

const obj = new Proxy({}, handler);
obj.a = 1; // 日志: Setting a to 1
console.log('a' in obj); // 日志: Checking if a exists
delete obj.a; // 日志: Deleting a

5. 验证代理

使用Proxy实现数据验证:

const validator = {
  set(target, prop, value) {
    if (prop === 'age') {
      if (typeof value !== 'number' || value <= 0) {
        throw new TypeError('Age must be a positive number');
      }
    }
    target[prop] = value;
    return true;
  }
};

const person = new Proxy({}, validator);
person.age = 30; // 正常
person.age = 'thirty'; // 抛出TypeError

6. 自动填充对象

实现一个自动填充默认值的代理:

const autoFiller = {
  get(target, prop) {
    if (!(prop in target)) {
      target[prop] = {};
    }
    return target[prop];
  }
};

const obj = new Proxy({}, autoFiller);
obj.a.b.c = 'value'; // 自动创建嵌套结构
console.log(obj); // { a: { b: { c: 'value' } } }

7. 负索引数组

使用Proxy实现类似Python的负索引数组:

function createNegativeArray(array) {
  return new Proxy(array, {
    get(target, prop, receiver) {
      if (typeof prop === 'string') {
        const index = parseInt(prop, 10);
        if (index < 0) {
          prop = target.length + index;
        }
      }
      return Reflect.get(target, prop, receiver);
    }
  });
}

const array = createNegativeArray([1, 2, 3, 4, 5]);
console.log(array[-1]); // 5
console.log(array[-2]); // 4

8. 方法链代理

实现一个流畅的方法链代理:

const chainable = {
  get(target, prop) {
    if (prop in target) {
      return target[prop];
    }
    
    return function(...args) {
      console.log(`Called ${prop} with args: ${args}`);
      return target; // 返回自身以实现链式调用
    };
  }
};

const api = new Proxy({}, chainable);
api.method1().method2(1, 2).method3('a', 'b');
// 输出:
// Called method1 with args: 
// Called method2 with args: 1,2
// Called method3 with args: a,b

9. 性能考虑

虽然Proxy功能强大,但需要注意:

  1. Proxy操作比直接对象访问慢
  2. 某些操作无法被拦截(如Date.prototype.getTime()
  3. 不是所有浏览器都完全支持所有Proxy特性

10. 实际应用场景

  1. 数据验证​:在设置属性值时进行验证
  2. 日志记录​:跟踪对象的所有操作
  3. 性能监控​:测量方法调用时间
  4. 自动保存​:在数据变更时自动保存到后端
  5. 虚拟属性​:动态计算属性值
  6. API封装​:简化复杂API的使用
  7. 权限控制​:限制对某些属性的访问

Proxy是JavaScript元编程的强大工具,合理使用可以极大地增强代码的灵活性和可维护性。

到此这篇关于JavaScript动态代理用法的完整指南的文章就介绍到这了,更多相关JavaScript动态代理用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • bootstrap学习笔记之初识bootstrap

    bootstrap学习笔记之初识bootstrap

    Bootstrap是一款目前非常流行的前端框架,简单的说,就是html,css,javascript的工具集。本文给大家介绍bootstrap学习笔记之初识bootstrap,感兴趣的朋友一起学习吧
    2016-06-06
  • javascript面向对象三大特征之继承实例详解

    javascript面向对象三大特征之继承实例详解

    这篇文章主要介绍了javascript面向对象三大特征之继承,简单描述了继承的概念、原理,并结合实例形式详细分析了继承的常见实现方法与操作注意事项,需要的朋友可以参考下
    2019-07-07
  • 5个经过实战验证的JavaScript性能优化技巧分享

    5个经过实战验证的JavaScript性能优化技巧分享

    在现代Web开发中,JavaScript性能优化是每个开发者都必须面对的挑战,本文将揭示5个经过实战验证的JavaScript性能优化技巧,其中第三个技巧更是被90%的开发者所忽视,需要的朋友可以参考下
    2026-03-03
  • uni-app基本的数据绑定v-bind,v-for,v-on:click详解

    uni-app基本的数据绑定v-bind,v-for,v-on:click详解

    这篇文章主要介绍了uni-app基本的数据绑定v-bind,v-for,v-on:click,本文通过示例代码给大家介绍的非常详细,需要的朋友可以参考下
    2024-08-08
  • 使用js简单实现了tree树菜单

    使用js简单实现了tree树菜单

    使用js简单实现了树菜单!具体实现实例代码如下,相信自己你一定可以实现的更好
    2013-11-11
  • 微信小程序使用wx.request请求服务器json数据并渲染到页面操作示例

    微信小程序使用wx.request请求服务器json数据并渲染到页面操作示例

    这篇文章主要介绍了微信小程序使用wx.request请求服务器json数据并渲染到页面操作,结合实例形式分析了微信小程序使用wx.request发送网络请求及返回结果渲染到wxml界面相关操作技巧,需要的朋友可以参考下
    2019-03-03
  • 微信小程序canvas开发水果老虎机的思路详解

    微信小程序canvas开发水果老虎机的思路详解

    这篇文章主要介绍了微信小程序canvas开发水果老虎机的思路,本文通过思路代码分步骤给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-02-02
  • 《JavaScript函数式编程》读后感

    《JavaScript函数式编程》读后感

    这里给大家分享的是个人在最近看《JavaScript函数式编程》一书的第一章之后的一些感触和理解,这里记录下来,有需要的小伙伴可以参考下
    2015-08-08
  • JavaScript判断是不是数组的四种方法总结

    JavaScript判断是不是数组的四种方法总结

    我们在日常开发中,常常有判断某值类型需求,这篇文章主要介绍了JavaScript判断是不是数组的四种方法,每种方法都给出了详细的代码示例,需要的朋友可以参考下
    2026-04-04
  • JS作用域深度解析

    JS作用域深度解析

    这篇文章主要为大家详细介绍了JS作用域,剖析JS的作用域问题,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12

最新评论