iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > html >ES6中Proxy使用场景有哪些
  • 670
分享到

ES6中Proxy使用场景有哪些

2024-04-02 19:04:59 670人浏览 独家记忆
摘要

小编给大家分享一下es6中Proxy使用场景有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!ES6 中的箭头函数、数组解构、

小编给大家分享一下es6中Proxy使用场景有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

ES6 中的箭头函数、数组解构、rest 参数等特性一经实现就广为流传,但类似 Proxy 这样的特性却很少见到有开发者在使用,一方面在于浏览器的兼容性,另一方面也在于要想发挥这些特性的优势需要开发者深入地理解其使用场景。就我个人而言是非常喜欢 ES6 的 Proxy,因为它让我们以简洁易懂的方式控制了外部对对象的访问。在下文中,首先我会介绍 Proxy 的使用方式,然后列举具体实例解释 Proxy 的使用场景。

Proxy,见名知意,其功能非常类似于设计模式中的代理模式,该模式常用于三个方面:

  1. 拦截和监视外部对对象的访问

  2. 降低函数或类的复杂度

  3. 在复杂操作前对操作进行校验或对所需资源进行管理

在支持 Proxy 的浏览器环境中,Proxy 是一个全局对象,可以直接使用。Proxy(target, handler) 是一个构造函数,target 是被代理的对象,handlder 是声明了各类代理操作的对象,最终返回一个代理对象。外界每次通过代理对象访问 target 对象的属性时,就会经过 handler 对象,从这个流程来看,代理对象很类似 middleware(中间件)。那么 Proxy 可以拦截什么操作呢?最常见的就是 get(读取)、set(修改)对象属性等操作,完整的可拦截操作列表请点击这里。此外,Proxy 对象还提供了一个 revoke 方法,可以随时注销所有的代理操作。在我们正式介绍 Proxy 之前,建议你对 Reflect 有一定的了解,它也是一个 ES6 新增的全局对象,详细信息请参考MDN Reflect。

Basic

const target = { 
  name: 'Billy Bob',
  age: 15
};

const handler = { 
  get(target, key, proxy) {
    const today = new Date();
    console.log(`GET request made for ${key} at ${today}`);

    return Reflect.get(target, key, proxy);
  }
};

const proxy = new Proxy(target, handler);
proxy.name;
// => "GET request made for name at Thu Jul 21 2016 15:26:20 GMT+0800 (CST)"
// => "Billy Bob"

在上面的代码中,我们首先定义了一个被代理的目标对象 target,然后声明了包含所有代理操作的 handler 对象,接下来使用 Proxy(target, handler) 创建代理对象 proxy,此后所有使用 proxy 对 target 属性的访问都会经过 handler 的处理。

1. 抽离校验模块

让我们从一个简单的类型校验开始做起,这个示例演示了如何使用 Proxy 保障数据类型的准确性:

let numericDataStore = { 
  count: 0,
  amount: 1234,
  total: 14
};

numericDataStore = new Proxy(numericDataStore, { 
  set(target, key, value, proxy) {
    if (typeof value !== 'number') {
      throw Error("Properties in numericDataStore can only be numbers");
    }
    return Reflect.set(target, key, value, proxy);
  }
});

// 抛出错误,因为 "foo" 不是数值
numericDataStore.count = "foo";

// 赋值成功
numericDataStore.count = 333;

如果要直接为对象的所有属性开发一个校验器可能很快就会让代码结构变得臃肿,使用 Proxy 则可以将校验器从核心逻辑分离出来自成一体:

function createValidator(target, validator) { 
  return new Proxy(target, {
    _validator: validator,
    set(target, key, value, proxy) {
      if (target.hasOwnProperty(key)) {
        let validator = this._validator[key];
        if (!!validator(value)) {
          return Reflect.set(target, key, value, proxy);
        } else {
          throw Error(`Cannot set ${key} to ${value}. Invalid.`);
        }
      } else {
        throw Error(`${key} is not a valid property`)
      }
    }
  });
}

const personValidators = { 
  name(val) {
    return typeof val === 'string';
  },
  age(val) {
    return typeof age === 'number' && age > 18;
  }
}
class Person { 
  constructor(name, age) {
    this.name = name;
    this.age = age;
    return createValidator(this, personValidators);
  }
}

const bill = new Person('Bill', 25);

// 以下操作都会报错
bill.name = 0; 
bill.age = 'Bill'; 
bill.age = 15;

通过校验器和主逻辑的分离,你可以无限扩展 personValidators 校验器的内容,而不会对相关的类或函数造成直接破坏。更复杂一点,我们还可以使用 Proxy 模拟类型检查,检查函数是否接收了类型和数量都正确的参数:

let obj = { 
  pickyMethodOne: function(obj, str, num) {  },
  pickyMethodTwo: function(num, obj) {  }
};

const argTypes = { 
  pickyMethodOne: ["object", "string", "number"],
  pickyMethodTwo: ["number", "object"]
};

obj = new Proxy(obj, { 
  get: function(target, key, proxy) {
    var value = target[key];
    return function(...args) {
      var checkArgs = arGChecker(key, args, argTypes[key]);
      return Reflect.apply(value, target, args);
    };
  }
});

function argChecker(name, args, checkers) { 
  for (var idx = 0; idx < args.length; idx++) {
    var arg = args[idx];
    var type = checkers[idx];
    if (!arg || typeof arg !== type) {
      console.warn(`You are incorrectly implementing the signature of ${name}. Check param ${idx + 1}`);
    }
  }
}

obj.pickyMethodOne(); 
// > You are incorrectly implementing the signature of pickyMethodOne. Check param 1
// > You are incorrectly implementing the signature of pickyMethodOne. Check param 2
// > You are incorrectly implementing the signature of pickyMethodOne. Check param 3

obj.pickyMethodTwo("wopdopadoo", {}); 
// > You are incorrectly implementing the signature of pickyMethodTwo. Check param 1

// No warnings logged
obj.pickyMethodOne({}, "a little string", 123); 
obj.pickyMethodOne(123, {});

2. 私有属性

javascript 或其他语言中,大家会约定俗成地在变量名之前添加下划线 _ 来表明这是一个私有属性(并不是真正的私有),但我们无法保证真的没人会去访问或修改它。在下面的代码中,我们声明了一个私有的 apiKey,便于 api 这个对象内部的方法调用,但不希望从外部也能够访问 api._apiKey:

var api = { 
  _apiKey: '123abc456def',
  
  getUsers: function(){}, 
  getUser: function(userId){}, 
  setUser: function(userId, config){}
};

// logs '123abc456def';
console.log("An apiKey we want to keep private", api._apiKey);

// get and mutate _apiKeys as desired
var apiKey = api._apiKey; 
api._apiKey = '987654321';

很显然,约定俗成是没有束缚力的。使用 ES6 Proxy 我们就可以实现真实的私有变量了,下面针对不同的读取方式演示两个不同的私有化方法。第一种方法是使用 set / get 拦截读写请求并返回 undefined:

let api = { 
  _apiKey: '123abc456def',
  getUsers: function(){ }, 
  getUser: function(userId){ }, 
  setUser: function(userId, config){ }
};

const RESTRICTED = ['_apiKey'];
api = new Proxy(api, { 
  get(target, key, proxy) {
    if(RESTRICTED.indexOf(key) > -1) {
      throw Error(`${key} is restricted. Please see api documentation for further info.`);
    }
    return Reflect.get(target, key, proxy);
  },
  set(target, key, value, proxy) {
    if(RESTRICTED.indexOf(key) > -1) {
      throw Error(`${key} is restricted. Please see api documentation for further info.`);
    }
    return Reflect.get(target, key, value, proxy);
  }
});

// 以下操作都会抛出错误
console.log(api._apiKey);
api._apiKey = '987654321';

第二种方法是使用 has 拦截 in 操作:

var api = { 
  _apiKey: '123abc456def',
  getUsers: function(){ }, 
  getUser: function(userId){ }, 
  setUser: function(userId, config){ }
};

const RESTRICTED = ['_apiKey'];
api = new Proxy(api, { 
  has(target, key) {
    return (RESTRICTED.indexOf(key) > -1) ?
      false :
      Reflect.has(target, key);
  }
});

// these log false, and `for in` iterators will ignore _apiKey
console.log("_apiKey" in api);

for (var key in api) { 
  if (api.hasOwnProperty(key) && key === "_apiKey") {
    console.log("This will never be logged because the proxy obscures _apiKey...")
  }
}

3. 访问日志

对于那些调用频繁、运行缓慢或占用执行环境资源较多的属性或接口,开发者会希望记录它们的使用情况或性能表现,这个时候就可以使用 Proxy 充当中间件的角色,轻而易举实现日志功能:

let api = { 
  _apiKey: '123abc456def',
  getUsers: function() {  },
  getUser: function(userId) {  },
  setUser: function(userId, config) {  }
};

function logMethodAsync(timestamp, method) { 
  setTimeout(function() {
    console.log(`${timestamp} - Logging ${method} request asynchronously.`);
  }, 0)
}

api = new Proxy(api, { 
  get: function(target, key, proxy) {
    var value = target[key];
    return function(...arguments) {
      logMethodAsync(new Date(), key);
      return Reflect.apply(value, target, arguments);
    };
  }
});

api.getUsers();

4. 预警和拦截

假设你不想让其他开发者删除 nodelete 属性,还想让调用 oldMethod 的开发者了解到这个方法已经被废弃了,或者告诉开发者不要修改 doNotChange 属性,那么就可以使用 Proxy 来实现:

let dataStore = { 
  noDelete: 1235,
  oldMethod: function() { },
  doNotChange: "tried and true"
};

const NODELETE = ['noDelete']; 
const NOCHANGE = ['doNotChange'];
const DEPRECATED = ['oldMethod']; 

dataStore = new Proxy(dataStore, { 
  set(target, key, value, proxy) {
    if (NOCHANGE.includes(key)) {
      throw Error(`Error! ${key} is immutable.`);
    }
    return Reflect.set(target, key, value, proxy);
  },
  deleteProperty(target, key) {
    if (NODELETE.includes(key)) {
      throw Error(`Error! ${key} cannot be deleted.`);
    }
    return Reflect.deleteProperty(target, key);

  },
  get(target, key, proxy) {
    if (DEPRECATED.includes(key)) {
      console.warn(`Warning! ${key} is deprecated.`);
    }
    var val = target[key];

    return typeof val === 'function' ?
      function(...args) {
        Reflect.apply(target[key], target, args);
      } :
      val;
  }
});

// these will throw errors or log warnings, respectively
dataStore.doNotChange = "foo"; 
delete dataStore.noDelete; 
dataStore.oldMethod();

5. 过滤操作

某些操作会非常占用资源,比如传输大文件,这个时候如果文件已经在分块发送了,就不需要在对新的请求作出相应(非绝对),这个时候就可以使用 Proxy 对当请求进行特征检测,并根据特征过滤出哪些是不需要响应的,哪些是需要响应的。下面的代码简单演示了过滤特征的方式,并不是完整代码,相信大家会理解其中的妙处:

let obj = { 
  getGiantFile: function(fileId) { }
};

obj = new Proxy(obj, { 
  get(target, key, proxy) {
    return function(...args) {
      const id = args[0];
      let isEnroute = checkEnroute(id);
      let isDownloading = checkStatus(id);   
      let cached = getCached(id);

      if (isEnroute || isDownloading) {
        return false;
      }
      if (cached) {
        return cached;
      }
      return Reflect.apply(target[key], target, args);
    }
  }
});

6. 中断代理

Proxy 支持随时取消对 target 的代理,这一操作常用于完全封闭对数据或接口的访问。在下面的示例中,我们使用了 Proxy.revocable 方法创建了可撤销代理的代理对象:

let sensitiveData = { username: 'devbryce' };
const {sensitiveData, revokeAccess} = Proxy.revocable(sensitiveData, handler);
function handleSuspectedHack(){ 
  revokeAccess();
}

// logs 'devbryce'
console.log(sensitiveData.username);
handleSuspectedHack();
// TypeError: Revoked
console.log(sensitiveData.username);

Decorator

ES7 中实现的 Decorator,相当于设计模式中的装饰器模式。如果简单地区分 Proxy 和 Decorator 的使用场景,可以概括为:Proxy 的核心作用是控制外界对被代理者内部的访问,Decorator 的核心作用是增强被装饰者的功能。只要在它们核心的使用场景上做好区别,那么像是访问日志这样的功能,虽然本文使用了 Proxy 实现,但也可以使用 Decorator 实现,开发者可以根据项目的需求、团队的规范、自己的偏好自由选择。

以上是“ES6中Proxy使用场景有哪些”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网html频道!

--结束END--

本文标题: ES6中Proxy使用场景有哪些

本文链接: https://www.lsjlt.com/news/77550.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • ES6中Proxy使用场景有哪些
    小编给大家分享一下ES6中Proxy使用场景有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!ES6 中的箭头函数、数组解构、...
    99+
    2024-04-02
  • vuex中有哪些使用场景
    这篇文章将为大家详细讲解有关vuex中有哪些使用场景,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。vue是什么Vue是一套用于构建用户界面的渐进式JavaScript框架,Vue与其它大型框架的区别是,使...
    99+
    2023-06-14
  • mq使用场景有哪些
    今天小编给大家分享一下mq使用场景有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。MQ的介绍及特点MQ 称为消息队列。消...
    99+
    2023-06-29
  • java中ThreadLocal的使用场景有哪些
    今天就跟大家聊聊有关java中ThreadLocal的使用场景有哪些,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。常用的java框架有哪些1.SpringMVC,Spring Web...
    99+
    2023-06-14
  • JavaScript中this的使用场景有哪些
    本篇文章给大家分享的是有关JavaScript中this的使用场景有哪些,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。JavaScript是什么JavaScript是一种直译式...
    99+
    2023-06-14
  • android ndk使用场景有哪些
    Android NDK主要用于以下几个场景:1. 提高性能:使用NDK可以将一些性能敏感的部分代码通过C或C++编写,以提高执行效率...
    99+
    2023-08-23
    android ndk
  • java中SPI的使用场景有哪些
    这期内容当中小编将会给大家带来有关java中SPI的使用场景有哪些,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Java的特点有哪些Java的特点有哪些1.Java语言作为静态面向对象编程语言的代表,实现...
    99+
    2023-06-14
  • redis中set的使用场景有哪些
    Redis中的Set数据结构常用于以下场景:1. 去重:Set中的元素是唯一的,可以用来存储一组唯一的值,比如用户的标签、IP地址等...
    99+
    2023-09-04
    redis set
  • redis中hash的使用场景有哪些
    存储对象的属性和值:可以将一个对象的属性和值存储在一个hash中,方便对对象进行存储和检索。 缓存数据:可以将一些需要频繁访...
    99+
    2024-04-03
    redis
  • Java中WeakHashMap的使用场景有哪些
    Java中WeakHashMap是一种可以自动删除不再使用的键值对的Map实现,它的使用场景包括:1. 缓存:WeakHashMap...
    99+
    2023-09-29
    Java
  • kafka的使用场景有哪些
    Kafka的使用场景有以下几个:1. 数据流处理:Kafka被广泛应用于数据流处理系统中,可用于处理实时流数据,并将数据传递给下游处...
    99+
    2023-09-14
    kafka
  • webworker的使用场景有哪些
    Web Worker 是一种可以在后台运行的 JavaScript 线程,主要用于处理高性能计算和长时间运行的任务,以避免阻塞主线程...
    99+
    2023-09-21
    webworker
  • android中广播的使用场景有哪些
    在Android中,广播(Broadcast)的使用场景包括以下几种:1. 系统级广播:系统级广播是由Android系统发送的广播,...
    99+
    2023-09-04
    android
  • node.js中有哪些适用场景
    这篇文章主要介绍了node.js中有哪些适用场景,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。 node.js是...
    99+
    2024-04-02
  • Redis中有哪些应用场景
    本篇内容介绍了“Redis中有哪些应用场景”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Redis数据类型...
    99+
    2024-04-02
  • mongodb典型使用场景有哪些
    MongoDB的典型使用场景包括:1. 实时分析和大数据处理:MongoDB的分布式架构和高可扩展性使其非常适合实时分析和大数据处理...
    99+
    2023-09-11
    mongodb
  • 前端H5中Video的使用场景有哪些
    这篇文章主要介绍前端H5中Video的使用场景有哪些,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1.原生H5 video标签<video id="mse" autopl...
    99+
    2023-06-09
  • ES6中Proxy有什么用
    这篇文章主要为大家展示了“ES6中Proxy有什么用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“ES6中Proxy有什么用”这篇文章吧。具体如下:Proxy ...
    99+
    2024-04-02
  • linux中lscpu命令的使用场景有哪些
    查看系统中CPU相关信息:使用lscpu命令可以查看系统中CPU的相关信息,包括CPU型号、架构、核心数、线程数、缓存大小等。 查...
    99+
    2024-03-07
    linux
  • Java中线程池的使用场景有哪些
    今天就跟大家聊聊有关Java中线程池的使用场景有哪些,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。 一、如下方式存在的问题new Thread() {&...
    99+
    2023-06-16
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作