Introduction to Use Cases of instance parsing ES6 Proxy, es6proxy

Source: Internet
Author: User
Tags hasownproperty

Introduction to Use Cases of instance parsing ES6 Proxy, es6proxy

Features such as Arrow functions, array deconstruct, and rest parameters in ES6 have been widely circulated once implemented. However, features such as Proxy are rarely used by developers, on the one hand, browser compatibility, on the other hand, developers need to have a deep understanding of the application scenarios to take advantage of these features. Personally, I like ES6 Proxy very much because it allows us to control external access to objects in a simple and easy-to-understand way. In the following section, I will first introduce how to use Proxy, and then list specific instances to explain the use cases of Proxy.

Proxy, known for its functionality, is very similar to the Proxy mode in the design mode. This mode is commonly used in three aspects:

  1. Intercept and monitor external access to objects
  2. Reduce the complexity of functions or classes
  3. Verify operations or manage required resources before complex operations

In a browser that supports Proxy, Proxy is a global object and can be directly used. Proxy (target, handler) is a constructor, target is the object to be Proxy, handlder is the object that declares various Proxy operations, and finally returns a Proxy object. Every time the outside world accesses the properties of the target object through the proxy object, it will go through the handler object. From this process, the proxy object is similar to middleware (middleware ). What operations can Proxy intercept? The most common operations are get (read) and set (modify) object attributes. Click here for the complete list of intercepted operations. In addition, the Proxy object provides a revoke method that can cancel all Proxy operations at any time. Before we officially introduce Proxy, we recommend that you have a certain understanding of Reflect, which is also a new Global Object added to ES6. For more information, see 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"

In the code above, we first define a target object to be Proxy, then declare the handler object containing all Proxy operations, and then use Proxy (target, handler) create a proxy object. After that, All accesses to the target attribute using the proxy will be processed by handler.

1. Extraction verification module

Let's start with a simple type verification. This example demonstrates how to use Proxy to ensure data type accuracy:

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) ;}}); // throw an error because "foo" is not a numeric value numericDataStore. count = "foo"; // The value is successfully assigned to numericDataStore. count = 333;

If you want to develop a checker directly for all the properties of the object, the code structure may quickly become bloated. Using Proxy, you can separate the checker from the core logic into one:

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); // The following operations will report an error. name = 0; bill. age = 'bill '; Bill. age = 15;

With the separation of the validator and the main logic, you can infinitely expand the content of the personValidators validator without directly damaging the related classes or functions. To be more complex, we can also use the Proxy to simulate the type check to check whether the function has received the correct parameters of the type and quantity:

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 3obj.pickyMethodTwo("wopdopadoo", {}); // > You are incorrectly implementing the signature of pickyMethodTwo. Check param 1// No warnings loggedobj.pickyMethodOne({}, "a little string", 123); obj.pickyMethodOne(123, {});

2. Private attributes

In JavaScript or other languages, it is customary to add an underscore _ before the variable name to indicate that this is a private property (not truly private ), but we cannot guarantee that no one will actually access or modify it. In the following code, we declare a private apiKey to facilitate calling the internal method of the api object, but do not want to access api. _ apiKey from the outside:

var api = {   _apiKey: '123abc456def',  /* mock methods that use this._apiKey */  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 desiredvar apiKey = api._apiKey; api._apiKey = '987654321';

Obviously, there is no constraint in conventions. With ES6 Proxy, we can implement real private variables. The following shows two different private variables for different reading methods. The first method is to use set/get to intercept read/write requests and return 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) ;}}); // The following operations will throw an error console. log (api. _ apiKey); api. _ apiKey = '000000 ';

The second method is to use has to intercept in operations:

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 _apiKeyconsole.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. Access logs

For attributes or interfaces that call frequently, run slowly, or consume a large amount of execution environment resources, developers want to record their usage or performance, in this case, you can use Proxy to act as the middleware and easily implement the log function:

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. Warning and Interception

Assume that you do not want other developers to delete the noDelete attribute. You also want the developers who call the oldMethod to learn that this method has been abandoned, or tell the developers not to modify the doNotChange attribute, then you can use Proxy to implement:

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, respectivelydataStore.doNotChange = "foo"; delete dataStore.noDelete; dataStore.oldMethod();

5. Filter Operations

Some operations occupy a lot of resources, such as transferring large files. If the file is already being sent in multiple parts, you do not need to make a corresponding (non-absolute) response to the new request ), in this case, you can use the Proxy to perform Feature Detection on the request and filter out what does not need to be responded and what needs to be responded based on the features. The following code demonstrates how to filter features. It is not a complete code. I believe you will understand the highlights of this Code:

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. Interrupt proxy

Proxy supports canceling Proxy on target at any time. This operation is usually used to completely close access to data or interfaces. In the following example, we used the Proxy. revocable method to create a Proxy object that can be undone:

let sensitiveData = { username: 'devbryce' };const {sensitiveData, revokeAccess} = Proxy.revocable(sensitiveData, handler);function handleSuspectedHack(){   revokeAccess();}// logs 'devbryce'console.log(sensitiveData.username);handleSuspectedHack();// TypeError: Revokedconsole.log(sensitiveData.username);

Decorator

The Decorator implemented in ES7 is equivalent to the Decorator mode in the design mode. If the use cases of Proxy and Decorator are divided in simple regions, the core function of Proxy is to control external access to the agent, the core function of Decorator is to enhance the functions of the Decorator. As long as they are well differentiated in their core application scenarios, functions such as access logs can be implemented using the Decorator, although the Proxy is used in this article, developers can freely choose based on project requirements, Team specifications, and their preferences.

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.