• Before this section introduces the Proxy section, you need to know a little about metaprogramming

I. Metaprogramming

  • Metaprogramming refers to the writing of a class of computer programsWrite or manipulate other programs (or themselves)As their data, orDo some work at run time that should be done at compile time. In many cases, it’s more efficient than writing all the code by hand.
  • The language in which the metaprogram is written is called the metalanguage, the language in which it is manipulated is called the target language,The ability of a language to be its own metalanguage is called reflection!
  • Starting with ES5, JS gets the pairSupport for Proxy,Reflect objects that allow you to intercept and define custom behavior for basic language operations, for example (attribute lookup, assignment, enumeration, function call, etc.),With these two objects, Proxy and Reflect, you can do metaprogramming in JS

Summary of two.

1. Basic meaning

  • Proxy Is used to modify certain operationsThe default behaviorIs equivalent to making changes at the language level, so it belongs toMeta Programming, or programming a programming language
  • Proxy can be understood asSet up an intercept in front of the target, external access to the object, must first pass this layer of interception.
  • So you can compareOutside access to filter and rewriteThe original meaning of Proxy is Proxy. In JS, Proxy is used to Proxy some operations, as a Proxy
  • A simple example of intercepting a GET read operation
 // Declare an interceptor object with the properties of the object to be listened on
 var handler={
  get:function(target,name){
   if(target.hasOwnProperty(name)){
    return The 'object contains${name}Attribute `
 }else{  return 'object has no${name}Attribute `  }  }  }  // Bind an interceptor to an obj object and create an instance of the interceptor  var obj={name:'yiye'.f:(a)= >{}}  var p=new Proxy(obj,handler)  // 1. Use the properties of the Proxy instance directly  console.log(p.name);// The object contains the name attribute  console.log(p.age);// The object has no age attribute  // 1.2 Assign a value to the Proxy instance (but it was intercepted, so the change does not take effect unless the Proxy tries to change it)  p.name="Change"  console.log(p.name);// The object contains the name attribute  // 2. There is no intercepting function, so it is not a function  console.log(p.f());//p.f is not a function  // 3. Calling a function whose object does not exist prompts that the variable is not a function  console.log(p.foo());//p.foo is not a function Copy the code
  • You can bind a Proxy to an object as an object propertyvar obj={proxy:new Proxy(target,handler)}
  • Proxy has prototype objects that can also be used as other objects, object.create (obj)
 var handler={
  get:function(target,proxyKey){
   return 35
  }
 }
 var a={name:'hh'}  // Use the proxy in the form of j.proxy.xxx  New Proxy(obj,handler  var obj={proxy:new Proxy(Object.create(a),handler)}  console.log(obj.proxy.name);/ / 35  console.log(obj.proxy.age);/ / 35 Copy the code

Parameters of 2.

  • New Proxy(target,handler) takes two parameters, representing the target object and the internal listener object, and handler is an empty {} objectIndicates that interception is not set, which is equivalent to directly accessing the original object
  • Handler internal listening method isThe set ofThere are three parameters, (target,name,value),Target indicates the target object, name indicates the attribute name, and value indicates the value
  • The number and meaning of parameters vary depending on how the handler listener listens
  • Here is an example of listening for a set operation
 // Declare an interceptor
 var handler={
 set:function(target,name,value){
  target[name]="set+"+value;
 }
 }  // Bind an interceptor to an obj object and create an instance of the interceptor  var obj={name:'yiye'.f:(a)= >{}}  var p=new Proxy(obj,handler)  // 1. Use the properties of the Proxy instance directly  console.log(p.name);//yiye  // 1.2 Assign a value to the Proxy instance  p.name="Change"  console.log(p.name);/ / set + change Copy the code

Proxy as a design pattern

  • There is a design pattern in programming that isProxy Pattern
  • An agent is a category that canAs an interface to something else, agents can act as interfaces to anything: network connections, large objects in memory, files or other resources that are expensive or impossible to copy.
  • Reference counting in the garbage collection mechanism uses the proxy pattern
  • When an object is referenced, proxies are created, and each proxy references the object. The operations that are performed on the agent are passed to the object.Once all the agents are gone, the object is cleaned up and garbage collected

Proxy instance methods

  • There are 13 methods for proxy instance, and only a few are used here.

1. get()

  • The get method is used to intercept object propertiesRead operationTakes three arguments, the target object, the property name, andProxy instance itself(strictly speaking, the object for which the action is performed), the last of which is optional.
  • Access properties that do not exist on the target object,It also performs get reads that the proxy listens for.If there is no interceptor, then undefined is returned
  • The listener's get method can be inherited!
 var a={name:'a'}
 var proxy=new Proxy(a,{
  get(target,name,value){
   if(target.hasOwnProperty(name)) return 'get+'+target[name]
   return 'This property does not exist'
 }  })  var child=Object.create(proxy)  console.log(child);//{name: "eee",__proto__:Proxy}, where Proxy is the listener object, not the function  console.log(child.__proto__);// This attribute does not exist  console.log(child.prototype);// This attribute does not exist  // Point to the property of the prototype object when the object child does not have a property name  console.log(child.name);//get+a  // Add an attribute name to the object, which points to the attribute of the object itself  child.name='eee'  console.log(child.name);//eee Copy the code
  • Use proxy to complete chained calls
 var pipe = function (value) {
  var funcStack = []; // Store the function
  var oproxy = new Proxy({}, {   get : function (pipeObject, fnName) {
    if (fnName === 'get') {
 return funcStack.reduce(function (val, fn) {  return fn(val);  },value);  }  funcStack.push(window[fnName]);  return oproxy; // Return proxy  }  });  return oproxy; // Return proxy  }   var double = n= > n * 2;  var pow = n= > n * n;  var reverseInt = n= > n.toString().split("").reverse().join("") | 0; // Reverse the number  // reverseInt(pow(double(3))); //3*2=6,6*6=36, the reverse order of 36 is 63  console.log(pipe(3).double.pow.reverseInt.get) / / 63 Copy the code

2. set()

  • The set method takes four arguments, which areThe target object, property name, property value, proxy instance itself, and the last parameter is optional
  • An example:An error occurs when the value of the age attribute is greater than 200
 var obj={age:10.name:'yiye'}
 var person=new Proxy(obj,{
  set:function(newobj,name,val,pro){
   if(name==='age') {    if(val>200) { throw new Error("this age is too max")  }  }  newobj[name]=val;/ / update  }  })  console.log(person);//Proxy {age: 10, name: "yiye"}  console.log(obj);//{age: 10, name: "yiye"}   // Modify the properties of the target object directly  obj.age=210  console.log(obj);//{age: 210, name: "yiye"}  console.log(person);//Proxy {age: 210, name: "yiye"}   // Modify the properties of the proxy object.  person.age=20;// No error will be reported, and the value will remain  console.log(person);//Proxy {age: 20, name: "yiye"}  console.log(obj);//{age: 20, name: "yiye"}  // person.age=220; // The limit is exceeded  console.log(obj); //Uncaught Error: this age is too max  console.log(person); //Uncaught Error: this age is too max Copy the code

limit

  • When a property of an objectNo, then the set listener will be invalidated
 var obj={age:10.name:'yi'}
 Object.defineProperty(obj,'foo', {  writable:false.  value:'foo'
 })
 var person=new Proxy(obj,{  set:function(target,name,value,receiver){  target[name]="set+"+value;  }  })  console.log(person);//Proxy {age: 10, name: "yi", foo: "foo"}  // Modify the foo attribute  person.foo="f"  // However, the change does not take effect.  console.log(person);//Proxy {age: 10, name: "yi", foo: "foo"}   // Modify other attributes at this time, it will take effect!  person.age=10000  person.name='good'  console.log(person);//Proxy {age: "set+10000", name: "set+good", foo: "foo"} Copy the code

3. apply()

  • The apply method intercepts three types of function calls. , call binding, apply binding!
  • Accepts three parameters, namely the target object, the context of the target object, and the array of parameters of the target object
 // 1. Create a proxy that listens for apply operations
 var sum=function(a,b){
  return a+b
 }
 var proxy=new Proxy(sum,{
 apply:function(target,ctx,args){  console.log(target,ctx,args);  / *ƒ (a, b) { return a+b  } undefined (2) [1, 2] * /  returnsum(... args); }  })  // 2. Function call  console.log(proxy(1.2));/ / 3  // 3  console.log(proxy.call(null.2.3));/ / 5  // 4  console.log(proxy.apply(null[4.5]));/ / 9 Copy the code

3. Proxy. Revocable ()

  • Proxy.revocable() returns a cancelable Proxy instance
  • The return is an object, object ofThe proxy attribute is the proxy instance, and the REVOKE attribute is a function that cancellations the proxy instanceIf you access the proxy instance after executing REVOKE, an error is thrown
 var obj={age:22.name:'ww'}
 var handler={}
 var {proxy,revoke}=Proxy.revocable(obj,handler);
 console.log(proxy);//Proxy {age: 22, name: "ww"}
 // Revoke the agent with revoke
 revoke();  console.log(proxy);//Proxy {}  // If you try to access the properties of the proxy instance again, an error message will be displayed indicating that the proxy has been canceled  console.log(proxy.age);// Cannot perform 'get' on a proxy that has been revoked Copy the code

A. This b. this C. this D. this

  • The proxy does the proxy, but this proxyDoes not change the direction of this
  • If you do not bind the reference to this in the listening method, then the rule to which this refers is used
 var obj={
  m:function(){
   console.log(this===proxy)
  }
 }
 var handler={}  var proxy=new Proxy(obj,handler)  // The obj object calls the m attribute method, so internally this refers to obj  obj.m();//false  // Proxy instance object calls m property method, so this refers to proxy internally  proxy.m();//true Copy the code
  • If you need to bind this to the target target object, then
 const target = new Date('2015-01-01');
 const handler = {
  get(target, prop) {
   if (prop === 'getDate') {
    return target.getDate.bind(target);
 }  return Reflect.get(target, prop);  }  };  const proxy = new Proxy(target, handler);  console.log(target.getDate());/ / 1  // Even if the method is called from the proxy object, this inside the property still points to the target object  console.log(proxy.getDate()) / / 1 Copy the code