The introduction

EcmaScript 2015 introduces two new built-in modules, Proxy and Reflect.

We can use Proxy and Reflect to proxy-hijack objects, similar to the object.defineProperty () effect in Es 5, but Reflect & Proxy is far more powerful.

Most developers are aware of the two new built-in modules in Es6, but you may not be sure why Proxy should be used in conjunction with Reflect.

Here, the article through a few easy to understand examples to tell about their complementary relationship.

Front knowledge

  • Proxy, a built-in set of “traps” for creating a Proxy for an object that intercepts and customizes basic operations (such as property lookup, assignment, enumeration, function calls, etc.).

  • Reflect, which provides methods to intercept JavaScript operations. These methods are the same as Proxy methods.

In simple terms, we can use Reflect to intercept the original JavaScript action by creating a Proxy object for the original object via Proxy.

If you don’t know &, then go to MDN to learn their knowledge.

After all, the Core responsive modules of VueJs/Core are based on these two apis.

Use Proxy alone

To start our first example, let’s use Proxy alone to cook a simple appetizer:

const obj = {
  name: 'wang.haoyu'};const proxy = new Proxy(obj, {
  // In the get trap, target represents the original object. Key represents the name of the property accessed
  get(target, key) {
    console.log('Hijacking your data access' + key);
    return target[key]
  },
});

proxy.name // Hijack your data access name -> wang.haoyu
Copy the code

As simple as it seems, we create a Proxy based on an OBJ object using the Proxy and declare a GET trap in the Proxy.

When we access proxy.name, we actually trigger the corresponding GET trap. It executes the logic in the GET trap, and at the same time executes the logic in the corresponding trap, and finally returns the corresponding target[key], which is called wang.haoyu.

The Proxy of the receiver

Everything in the Demo above seems to be going smoothly, right? Careful students may find that there is an extra parameter receiver in Proxy GET trap in reading Proxy MDN document.

So what does the word receiver mean? Most students will understand it as a proxy object, but this is incomplete.

Let’s also start with a simple example:

const obj = {
  name: 'wang.haoyu'};const proxy = new Proxy(obj, {
  // In the get trap, target represents the original object. Key represents the name of the property accessed
  get(target, key, receiver) {
    console.log(receiver === proxy);
    returntarget[key]; }});// log: true
proxy.name;
Copy the code

In the example above, we receive the parameter Receiver on the Get trap of the Proxy instance object.

Also, we print console.log(receiver === proxy) inside the trap; It prints true, indicating that the receiver is indeed equal to the proxy object.

So receiver does represent a proxy object, but that’s just one of the cases that receiver represents.

Let’s look at another example:

const parent = {
  get value() {
    return '19Qingfeng'; }};const proxy = new Proxy(parent, {
  // In the get trap, target represents the original object. Key represents the name of the property accessed
  get(target, key, receiver) {
    console.log(receiver === proxy);
    returntarget[key]; }});const obj = {
  name: 'wang.haoyu'};// set obj to inherit from parent's proxy object
Object.setPrototypeOf(obj, proxy);

// log: false
obj.value
Copy the code

In this article, I elaborate on the “masking” effects of the get/set accessors that appear on the prototype. I’m not going to go into that.

As you can see, the above code also prints console.log(receiver === proxy) on the get trap of the proxy object; The result is false.

So you can think a little bit about what a receiver is here. In fact, this is also the significance of the third receiver in proxy get trap.

It is intended to pass the correct caller pointing, as you can see in the code below:

.const proxy = new Proxy(parent, {
  // In the get trap, target represents the original object. Key represents the name of the property accessed
  get(target, key, receiver){-console.log(receiver === proxy) // log:false
+   console.log(receiver === obj) // log:true
    returntarget[key]; }}); .Copy the code

In fact, to put it simply, the meaning of receiver in get trap is to correctly transfer the context in the trap.

When it comes to property access, don’t forget that get traps also trigger corresponding property accessors, known as GET accessor methods.

We can clearly see that the above receiver represents the object inherited from Proxy, namely OBJ.

See here, we understand that the receiver of get trap in Proxy not only represents the Proxy object itself, but also may represent the object that inherits Proxy.

In essence, it is to ensure proper context access for the caller in the trap function, such as the receiver pointing to OBj.

Of course, you should not confuse Revceiver with the this keyword in the get trap, which refers to the handler object of the broker.

Such as:

const parent = {
  get value() {
    return '19Qingfeng'; }};const handler = {
  get(target, key, receiver) {
    console.log(this === handler); // log: true
    console.log(receiver === obj); // log: true
    returntarget[key]; }};const proxy = new Proxy(parent, handler);

const obj = {
  name: 'wang.haoyu'};// set obj to inherit from parent's proxy object
Object.setPrototypeOf(obj, proxy);

// log: false
obj.value
Copy the code

Reflect the receiver

Now that we know the get trap receiver in Proxy, let’s talk about the Get trap receiver in the Reflect API.

We know that in Proxy (we will use get trap as an example below), the third parameter receiver represents the Proxy object itself or the object that inherits from the Proxy object, and it represents the correct context when the trap is triggered.

const parent = {
  name: '19Qingfeng'.get value() {
    return this.name; }};const handler = {
  get(target, key, receiver) {
    return Reflect.get(target, key);
    // return target[key]}};const proxy = new Proxy(parent, handler);

const obj = {
  name: 'wang.haoyu'};// set obj to inherit from parent's proxy object
Object.setPrototypeOf(obj, proxy);

// log: false
console.log(obj.value);
Copy the code

Let’s take a look at the code above:

  • When we call obj. Value, there is no value attribute in obj itself.

  • It inherits the proxy object with the value attribute access operator, so masking will occur.

  • Access to the Get Value () property on the proxy is triggered.

  • At the same time, a GET trap is triggered because the value accessor on the proxy is accessed.

  • When entering the trap, target is the source object (parent) and key is value.

  • Get (target,key) is equivalent to target[key].

  • At this point, unwittingly this point in the get trap was secretly modified out!!

  • The caller’s obj was changed in the trap to the corresponding target, also known as parent.

  • It naturally prints out the corresponding parent[value], 19Qingfeng.

This is obviously not what we want. When I access obj.value, I want the corresponding name property on itself to be printed correctly, so called obj.value => wang.haoyu.

Then Relfect’s get trap receiver comes into its own.

const parent = {
  name: '19Qingfeng'.get value() {
    return this.name; }};const handler = {
  get(target, key, receiver){-return Reflect.get(target, key);
+   return Reflect.get(target, key, receiver); }};const proxy = new Proxy(parent, handler);

const obj = {
  name: 'wang.haoyu'};// set obj to inherit from parent's proxy object
Object.setPrototypeOf(obj, proxy);

// log: wang.haoyu
console.log(obj.value);
Copy the code

The code above is pretty simple:

  • First of all, we mentioned before that the receiver of get trap in Proxy not only represents the Proxy object itself, but also may represent the object inherited from the Proxy object, which needs to be differentiated from the caller. Here it is clearly obj pointing to inheritance and proxy objects.

  • Second, the third parameter in the Reflect get trap passes the Proxy receiver (obj) as a parameter, which changes the this reference on the call.

You can simply interpret reflect.get (target, key, receiver) as target[key].call(receiver), but this is pseudocode, but you’ll probably understand it better.

I’m sure you understand what receiver stands for in Relfect. Yes, it is the ability to modify this in property access to point to the receiver object passed in.

conclusion

I’m sure you’ve seen why Proxy should be used with Reflect. This is precisely why the correct this context reference is guaranteed when the hijacking of the proxy object is triggered.

Let’s recall a little bit more, for get traps (and of course, for sets and other traps involving receivers) :

  • The Receiver parameter accepted in Proxy represents the Proxy object itself or an object that inherits from the Proxy object.

  • The Receiver argument passed in Reflect modifies the this pointer when the original operation was performed.

At the end

That brings us to the end of the article, as to why Proxy & Reflect popped up.

I’ve been reading the Vue/ Corejs source code recently, and it happens to be heavily used internally by Proxy & Reflect.

Why Proxy should be used in conjunction with Reflect can be better understood by looking at VueJs’ dependency collection of reactive modules. But for those of you who are not familiar with VueJs, I don’t want to expand it.

Of course, RECENTLY I have been trying to write some periodic summaries while reading VueJs. Later, I will explain this process in detail in the article. If you are interested, you can continue to follow my latest developments

At the end, thank you to everyone. Let’s come on together