Not long ago, when I changed my job, I was asked about Proxy in an interview with a big factory. I had some impression in my mind, but I couldn’t tell you the specific method of use. The main reason was that I didn’t accumulate enough at ordinary times, so I hurried to make up for it.

Metaprogramming

  • Before we begin, let’s understand what Metaprogramming is, because proxies are objects based on Metaprogramming concepts, as wikipedia explains:

Metaprogramming is a programming technique in which computer programs have the ability to treat other programs as their data.

  • Which translates to

Metaprogramming is a programming technique that uses other programs as input data

Proxy

Proxy wraps objects and intercepts their behavior through traps

The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).

To put it simply, Proxy can be used to change the default operation of an object, such as self-defining set and GET, etc. Commonly used are the following:

  • apply
  • construct
  • defineProperty
  • deleteProperty
  • get
  • getOwnPropertyDescriptor
  • getPrototypeOf
  • has
  • isExtensible
  • ownKeys
  • preventExtensions
  • set
  • setPrototypeOf

There are three properties to understand in Proxy:

  • Target: an Object that virtualizes the proxy.
  • Handler: a Placeholder Object which contains traps.
  • Trap: The Methods that provide property access to the target object.

example

Create a Proxy object

let p = new Proxy(target, handler);
Copy the code

Create a new Employee object and output some of its properties

const employee = {
    firstName: 'Tapas',
    lastName: 'Adhikary'
};

console.group('employee');
console.log(employee.firstName);
console.log(employee.lastName);
console.log(employee.org);
console.log(employee.fullName);
console.groupEnd()
Copy the code

The output above looks like this:

employee
Tapas
Adhikary
undefined
undefined
Copy the code

Next use Proxy to change some default behaviors:

  • Here is a handler that overwrites the get method. If the corresponding fieldName is not available in the target, you can customize the output:
let handler = {
    get: function(target, fieldName) {        

        if(fieldName === 'fullName' ) {
            return `${target.firstName} ${target.lastName}`;
        }

        return fieldName in target ?
            target[fieldName] :
                `No such property as, '${fieldName}'! `}};Copy the code
  • Step 2 create a Proxy object and pass in the previously defined Employee as target
let p = new Proxy(employee, handler);
Copy the code
  • The third step prints the properties of P
console.group('proxy');
console.log(p.firstName);
console.log(p.lastName);
console.log(p.org);
console.log(p.fullName);
console.groupEnd()
Copy the code

Then you can see that the output is not the same as before. The expected result and the actual result are as follows:

proxy
  Tapas
  Adhikary
  No such property as, 'org'!
  Tapas Adhikary
Copy the code

Here is an example of changing the set method to implement a validation: Create a new handler and rename it validator:

const validator = {
    set: function(obj, prop, value) {
        if (prop === 'age') {
            if(! Number.isInteger(value)) { throw new TypeError('Age is always an Integer, Please Correct it! ');
            }
            if(value < 0) {
                throw new TypeError('This is insane, a negative age? '); }}}};Copy the code

Create a new Proxy:

let pr = new Proxy(employee, validator);
Copy the code

You can then try to set an invalid property, such as:

pr.age = "test";Copy the code

You will get the following error:

Uncaught TypeError: Age is always an Integer, Please Correct it!
    at Object.set (<anonymous>:5:23)
    at <anonymous>:1:7
set @ VM2381:5
(anonymous) @ VM2434:1
Copy the code

Or:

Pr. Age = 1;Copy the code

Results:

Uncaught TypeError: This is insane, a negative age?
    at Object.set (<anonymous>:8:23)
    at <anonymous>:1:7
set @ VM2381:8
(anonymous) @ VM2531:1
Copy the code

As can be seen from the above, the function of Proxy is still very powerful and practical. The common application scenarios are as follows:

  • Protect ID field from deletion (override deleteProperty)
  • Value and write values (data binding) (override get, set)
  • Change the default behavior of the IN operation, etc

This points to the

  • It is important to note that the proxy changes the this reference in target. Once the proxy proxies for target, this in target refers to the proxy, not target.
const target = {
  get: function() { console.log(this === proxy); }}; const handler = {}; const proxy = new Proxy(target, handler); target.get() //false
proxy.get()  // true
Copy the code

Some internal attributes of native objects can only be obtained with the correct this, so the proxy cannot proxy these attributes of native objects.

const target = new Date();
const handler = {};
const proxy = new Proxy(target, handler);

proxy.getDate();
// TypeError: this is not a Date object.
Copy the code

For the above code, the getDate method is available only on the instance of the Date object, and an error is reported if this is not a Date. You can solve this problem by binding this to the original object.

const target = new Date('2019-07-11');
const handler = {
  get(target, prop) {
    if (prop === 'getDate') {
      return target.getDate.bind(target);
    }
    returnReflect.get(target, prop); }}; const proxy = new Proxy(target, handler); proxy.getDate() // 11Copy the code

This introduces another new ES6 object called Reflect, which is used to get default methods from objects, and will be discussed in the next article.

conclusion

  • Proxy can be used to change the default output of a method, such as not returning undefined if the get method fails to get a value, and adding validation and data binding to a set method.

  • reference

    Tapas Adhikary

    Proxy – JavaScript | MDN

    es6.ruanyifeng.com/#docs/proxy