SoFunction
Updated on 2025-04-03

A preliminary study on the use of Proxy, a highlight in Vue 3.0

Preface

Not long ago, at the VueConf TO 2018 conference held in Toronto from November 14 to 16, You Yuxi delivered a keynote speech called Vue3.0 Updates, elaborating on the update plan and direction of Vue3.0, saying that he had given up using it and chose to use a faster native Proxy!!

This will eliminate many of the limitations of the previous implementations based on : the inability to listen for attribute addition and removal, array index and length changes, and can support Map, Set, WeakMap, and WeakSet!

As a "front-end engineer", it is necessary to recommend a wave of Proxy!!

What is Proxy?

MDN This is described above - Proxy objects are used to define custom behaviors of basic operations (such as property search, assignment, enumeration, function calls, etc.).

The official description is always concise and concise, so it is not clear that it is...

In fact, it provides interception before the target object operation, which can filter and rewrite external operations and modify the default behavior of certain operations. In this way, we can not directly operate the object itself, but indirectly operate the object through the proxy object of the object to achieve the expected purpose~

What? Haven't made it clear yet? Let's look at an example below and it will be clear at a glance~

  let obj = {
   a : 1
  }
  let proxyObj = new Proxy(obj,{
    get : function (target,prop) {
      return prop in target ? target[prop] : 0
    },
    set : function (target,prop,value) {
      target[prop] = 888;
    }
  })
  
  ();    // 1
  ();    // 0

   = 666;
  ()     // 888

In the above example, we defined an object obj in advance, generated a proxyObj object through the Proxy constructor, and re-modified its set (write) and get (read) behaviors.

When we access the attribute that originally exists in the object, we will return the corresponding value in the original attribute. If we try to access a property that does not exist, it will return 0, that is, when we access, there is a property in the original object, so it will return 1. When we try to access the property that does not exist in the object, we will not return undefined, but return 0. When we try to set a new property value, we will always return 888. Therefore, even if we assign a value to 666, it will not take effect, and will still return 888!

grammar

The Proxy syntax provided by ES6 is very simple, and the usage is as follows:

let proxy = new Proxy(target, handler);

The parameter target is a target object wrapped in Proxy (can be any type of object, including native arrays, functions, or even another proxy). The parameter handler is also an object whose attribute is a function that defines the behavior of the proxy when an operation is performed, that is, a custom behavior.

The basic usage of Proxy is just like above. The difference is the difference between handler objects. The handler can be an empty object {}, which means that the proxy operation is the target operation on the target object, that is:

  let obj = {}
  
  let proxyObj = new Proxy(obj,{})
  
   = 1;
   = function () {
    ('it is a function')
  }

  (); // 1
  ();   // 1
  (())  // it is a function

But it should be noted that handler cannot be set to null, an error will be thrown - Cannot create proxy with a non-object as target or handler!

In order for Proxy to work, we cannot operate the object of the original object, that is, the target object (the above example is the obj object). We must operate on the Proxy instance (the above example is the proxyObj object), otherwise the expected effect will not be achieved. From the initial example, after we set the get method, the view continues to read an unexisting property b from the original object obj, and the result still returns undefined:

  ();   // 1
  ();     // undefined

For operations that can be set but not intercepted, the processing results of the proxy object will also be applied to the original target object target. How do you understand it? Let’s take the first example. We redefined the set method, and all attribute settings return 888. There is no special interception or processing for a special attribute (here refers to obj’s a property). Then the result after the operation = 666 will also act on the original target object (obj object), so the value of a of the obj object will also become 888!

   = 666;
  ( );  // 888
  ( );    // 888

API

Proxy in ES6 currently provides 13 proxy operations. Below I will summarize and organize several more commonly used APIs. Students who want to know other methods can check it out on their own:

--(target,property,receiver)

Used to intercept the object's read property operation, target refers to the target object, property is the acquired property name, receiver is Proxy or an object inheriting from Proxy, and is generally a Proxy instance.

let proxy = new Proxy({},{
  get : function (target,prop) {
    (`get ${prop}`);
    return 10;
  }
})
  
()  // get a
            // 10

We intercepted the read get operation of an empty object. When the property inside it is obtained, it will output get ${prop} and return 10;

let proxy = new Proxy({},{
  get : function (target,prop,receiver) {
      return receiver;
    }
  })

()  // Proxy{}
( === proxy) //true

The a property of the above proxy object is provided by the proxy object, so receiver points to the proxy object, so === proxy returns true.

Note that if the target attribute to be accessed is unwritable and unconfigurable, the returned value must be the same as the value of the target attribute, that is, it cannot be modified, otherwise an exception will be thrown ~

let obj = {};
(obj, "a", {
 configurable: false,
 enumerable: false,
 value: 10,
 writable: false
});

let proxy = new Proxy(obj,{
  get : function (target,prop) {
    return 20;
  }
})

()  // Uncaught TypeError

The a property in the above obj object is not writable and cannot be configured. We create an instance of proxy through Proxy and intercept its get operation. When we output it, an exception will be thrown. At this time, if we modify the return value of the get method the same as the value of the target property, that is, 10, we can eliminate the exception~

--(target, property, value, receiver)

Used to intercept the operation of setting attribute values. Compared with the get method, the parameters have one more value, that is, the attribute value to be set~

In strict mode, the set method needs to return a boolean value. Returning true means that the setting property has been successful. If false is returned and the setting property operation fails, a TypeError will be thrown.

let proxy = new Proxy({},{
  set : function (target,prop,value) {
    if( prop === 'count' ){
      if( typeof value === 'number'){
        ('success')
       target[prop] = value;
      }else{
       throw new Error('The variable is not an integer')
      }
    }
  }
})
  
  = '10';  // The variable is not an integer
 
  = 10;   // success

In the above, we have restricted the assignment of the count attribute in the target object by modifying the set method. We require that the assignment of the count attribute must be a number type data. If not, an error will be returned. The variable is not an integer. The first time we assign the string '10' to count, an exception was thrown, and the second time we assign the value to the number 10, and the printing is successful. Therefore, we can use the set method to do some data verification!

Similarly, if the target attribute is unwritable and unconfigurable, its value cannot be changed, that is, the assignment is invalid, as follows:

let obj = {};
(obj, "count", {
  configurable: false,
  enumerable: false,
  value: 10,
  writable: false
});

let proxy = new Proxy(obj,{
  set : function (target,prop,value) {
    target[prop] = 20;
  }
})

 = 20 ;
()  // 10

For the count property in the above obj object, we set it to be unmodified, and the default value is given to 10 , so even if it is assigned a value of 20 , the result still remains unchanged!

--(target, thisArg, argumentsList)

There are three parameters used to intercept the call of a function, namely the target object (function) target, the context object thisArg when called, and the parameter array argumentsList when called. This method can return any value.

target must be a function object, otherwise a TypeError will be thrown;

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

const handler = {
  apply: function(target, thisArg, argumentsList) {
   (`Calculate sum: ${argumentsList}`); 
   return target(argumentsList[0], argumentsList[1]) * 10;
  }
};

let proxy = new Proxy(sum, handler);

(sum(1, 2));   // 3
(proxy(1, 2));  // Calculate sum:1,2
              // 6

In fact, apply will also intercept the () and () of the target object, as well as the () operations, as follows:

((null, 3, 4));  // Calculate sum:3,4
                    // 14

((proxy, null, [5, 6]));  // Calculate sum: 5,6
                          // 22

--(target, argumentsList, newTarget)

construct is used to intercept the new operator. In order for the new operator to take effect on the generated Proxy object, the target object used to initialize the proxy must have the [[Construct]] internal method; it receives three parameters, the target object target, the constructor parameter list argumentsList, and the constructor used by the new command when the initial instance object is, that is, the p in the following example.

let p = new Proxy(function() {}, {
  construct: function(target, argumentsList, newTarget) {
   (newTarget === p );             // true
   ('called: ' + (', '));   // called:1,2
   return { value: ( argumentsList[0] + argumentsList[1] )* 10 };
  }
});

(new p(1,2).value);   // 30

In addition, the method must return an object, otherwise an exception will be thrown!

var p = new Proxy(function() {}, {
  construct: function(target, argumentsList, newTarget) {
   return 2
  }
});

(new p(1,2));  // Uncaught TypeError

--(target,prop)

The has method can be regarded as a hook for the in operation. When we judge whether the object has a certain property, this method will take effect. A typical operation is in. The method will receive two parameters: the target object target and the property prop to be checked, and returns a boolean value.

let p = new Proxy({}, {
  has: function(target, prop) {
   if( prop[0] === '_' ) {
   ('it is a private property')
   return false;
   }
   return true;
  }
});

('a' in p);   // true
('_a' in p )   // it is a private property
              // false

In the above example, we use the has method to hide the private attribute starting with the attribute underscore_, so that false will be returned when judging, so that it will not be discovered by the in operator~

Note that if a certain property of the target object itself cannot be configured, the property cannot be hidden by the proxy. If the target object is an unscalable object, the property of the object cannot be hidden by the proxy, otherwise a TypeError will be thrown.

let obj = { a : 1 };

(obj); // Make an object unscalable, that is, you can never add new attributes
let p = new Proxy(obj, {
 has: function(target, prop) {
 return false;
 }
});

('a' in p); // TypeError is thrown

Data binding

I have introduced so much above, which is also a preliminary understanding of Proxy. Then we can use Proxy to manually implement a very simple bidirectional binding of data (the implementation method can be found at the end of my previous article)~

It mainly depends on the implementation of functions, so I just waved my hand in the layout~

The page structure is as follows:

<!--html-->
<div >
  <h3 ></h3>
  <input type="text" />
</div>

It mainly depends on the logic:

//Get the node of the paragraphconst paragraph = ('paragraph');
//Get the input box nodeconst input = ('input');
  
//The data object that requires proxyconst data = {
 text: 'hello world'
}

const handler = {
 // Monitor the changes in text attributes in data set: function (target, prop, value) {
   if ( prop === 'text' ) {
        //Update the value        target[prop] = value;
        //Update view         = value;
         = value;
        return true;
   } else {
   return false;
   }
 }
}

//Add input listening event('input', function (e) {
   = ;  //Update the value of myText}, false)

//Construct a proxy objectconst myText = new Proxy(data,handler);

//Initialize the value = ;  

In the above, we created myText instance through Proxy, and updated the view changes by intercepting the text attribute set method in myText, achieving an extremely simple two-way data binding~

Summarize

After saying so much, Proxy is finally getting started. Although its syntax is very simple, it is not easy to actually exert its value. In addition, it is not a big deal in the compatibility of Proxy, so we don’t use many scenarios in actual application development, but it does not mean that it is not practical. In my opinion, we can use it for secondary processing of data, check the legality of data, and even proxy functions. More useful value is waiting for you to develop~

Besides, Vue3.0 is ready to be released, aren't you planning to learn it?

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.