Detailed explanation of ES6_Proxy

Proxy is an API introduced by ES6 for manipulating objects.

Proxy can intercept operations such as reading and function calling of the target object, and then perform operation processing. It does not operate the object directly, but operates through the proxy object of the object like the proxy mode. When performing these operations, some additional operations can be added.

basic usage

A Proxy object consists of two parts: target and handler. When generating an instance object through the Proxy constructor, these two parameters need to be provided. target is the target object, and handler is an object that declares the specified behavior of the proxy target.

let target = {
    name: 'Tom',
    age: 24
}
let handler = {
    get: function(target, key) {
        console.log('getting '+key);
        return target[key]; // 不是target.key
    },
    set: function(target, key, value) {
        console.log('setting '+key);
        target[key] = value;
    }
}
let proxy = new Proxy(target, handler)
proxy.name     // 实际执行 handler.get
proxy.age = 25 // 实际执行 handler.set
// getting name
// setting age
// 25
 

target can also be an empty object

let targetEpt = {}
let proxyEpt = new Proxy(targetEpt, handler)
// 调用 get 方法,此时目标对象为空,没有 name 属性
proxyEpt.name // getting name
// 调用 set 方法,向目标对象中添加了 name 属性
proxyEpt.name = 'Tom'
// setting name
// "Tom"
// 再次调用 get ,此时已经存在 name 属性
proxyEpt.name
// getting name
// "Tom"
 
// 通过构造函数新建实例时其实是对目标对象进行了浅拷贝,因此目标对象与代理对象会互相
// 影响
targetEpt)
// {name: "Tom"}
 
// handler 对象也可以为空,相当于不设置拦截操作,直接访问目标对象
let targetEmpty = {}
let proxyEmpty = new Proxy(targetEmpty,{})
proxyEmpty.name = "Tom"
targetEmpty) // {name: "Tom"}

Proxy instance method:

get( )

The get method is used to intercept the read operation of a property. It can receive three parameters, which are the target object , the property name and the Proxy instance itself (the operation behavior is the target object). The last parameter is optional .

var person = {
  name: "张三"
};

var proxy = new Proxy(person, {
  get: function(target, property) {
    if (property in target) {
      return target[property];
    } else {
      throw new ReferenceError("Property \"" + property + "\" does not exist.");
    }
  }
});

proxy.name // "张三"
proxy.age // 抛出一个错误

The above code indicates that if there is this attribute, the attribute value will be returned. If there is no attribute, an error will be reported. If there is no interception, undefined will be returned if there is no attribute.

The get method can also be inherited

let proto = new Proxy({}, {
  get(target, propertyKey, receiver) {
    console.log('GET ' + propertyKey);
    return target[propertyKey];
  }
});

let obj = Object.create(proto);
obj.foo // "GET foo"

In the above code, the interception operation is defined on the prototype object, so if the properties inherited by the obj object are read, the interception will take effect.

Using proxy, the operation (get) of reading a certain attribute can be transformed into executing a certain function, thereby realizing the chain operation of attributes.

var pipe = (function () {
  return function (value) {
    var funcStack = [];
    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 oproxy;
  }
}());

var double = n => n * 2;
var pow    = n => n * n;
var reverseInt = n => n.toString().split("").reverse().join("") | 0;

pipe(3).double.pow.reverseInt.get; // 63

If a property is not configurable (configurable) and not writable (writable), the Proxy cannot modify the property, otherwise an error will be reported when accessing the property through the Proxy object.

const target = Object.defineProperties({}, {
  foo: {
    value: 123,
    writable: false,
    configurable: false
  },
});

const handler = {
  get(target, propKey) {
    return 'abc';
  }
};

const proxy = new Proxy(target, handler);

proxy.foo
// TypeError: Invariant check failed

set()

The set method is used to intercept the assignment operation of a certain attribute. It can accept 4 parameters, which are the target object , the attribute name , the attribute value and the Proxy instance itself . The last parameter is optional .

Assuming that the Person object has an age attribute, which is an integer not greater than 200, then Proxy can be used to ensure that the age attribute value meets the requirements.

let validator = {
  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }

    // 对于满足条件的 age 属性以及其他属性,直接保存
    obj[prop] = value;
  }
};

let person = new Proxy({}, validator);

person.age = 100;

person.age // 100
person.age = 'young' // 报错
person.age = 300 // 报错

In the above code, since the storage function set is set, any assignment of the age attribute that does not meet the requirements will report an error, which is an implementation method of data verification. Using the set method, data binding can also be used, and the DOM will be automatically updated whenever the object changes.

Sometimes we can set internal properties on an object. The first character of the property name starts with an underscore, indicating that these properties should not be used externally, such as:

const handler = {
  get (target, key) {
    invariant(key, 'get');
    return target[key];
  },
  set (target, key, value) {
    invariant(key, 'set');
    target[key] = value;
    return true;
  }
};
function invariant (key, action) {
  if (key[0] === '_') {
    throw new Error(`Invalid attempt to ${action} private "${key}" property`);
  }
}
const target = {};
const proxy = new Proxy(target, handler);
proxy._prop
// Error: Invalid attempt to get private "_prop" property
proxy._prop = 'c'
// Error: Invalid attempt to set private "_prop" property

In the above code, as long as the first character of the read-write attribute name is an underscore, an error will be reported, thereby prohibiting the purpose of reading and writing internal attributes.

Guess you like

Origin blog.csdn.net/qq_41916378/article/details/110057178