zoukankan      html  css  js  c++  java
  • ES6----Proxy(一)

    Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

    听起来好像很绕,可以简单这样理解,Proxy相当于JavaScript对象的一个代理,通过Proxy的配置,可以实现对所代理的对象实现一些拦截和

    私有化定制。

    ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。

    1 var proxy = new Proxy(target, handler);

    通常,Proxy有以下几种形式:

     1 get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。
     2 set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
     3 has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
     4 deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
     5 ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
     6 getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
     7 defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
     8 preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
     9 getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
    10 isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
    11 setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
    12 apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
    13 construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。

    下面将会结合实例做具体介绍:

    1.get

    get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。

     1 var person = {
     2   name: "张三"
     3 };
     4 
     5 var proxy = new Proxy(person, {
     6   get: function(target, property) {
     7     if (property in target) {
     8       return target[property];
     9     } else {
    10       throw new ReferenceError("Property "" + property + "" does not exist.");
    11     }
    12   }
    13 });
    14 
    15 proxy.name // "张三"
    16 proxy.age // 抛出一个错误

    很简单,OK!

    升华以下,get方法可以继承。

    1 let proto = new Proxy({}, {
    2   get(target, propertyKey, receiver) {
    3     console.log('GET ' + propertyKey);
    4     return target[propertyKey];
    5   }
    6 });
    7 
    8 let obj = Object.create(proto);
    9 obj.foo // "GET foo"

    2.set

    set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。

    是不是和get方法大同小异,在读取被代理对象的属性时,该方法会执行;

     1 let validator = {
     2   set: function(obj, prop, value) {
     3     if (prop === 'age') {
     4       if (!Number.isInteger(value)) {
     5         throw new TypeError('The age is not an integer');
     6       }
     7       if (value > 200) {
     8         throw new RangeError('The age seems invalid');
     9       }
    10     }
    11 
    12     // 对于满足条件的 age 属性以及其他属性,直接保存
    13     obj[prop] = value;
    14   }
    15 };
    16 
    17 let person = new Proxy({}, validator);
    18 
    19 person.age = 100;
    20 
    21 person.age // 100
    22 person.age = 'young' // 报错
    23 person.age = 300 // 报错

    上面代码中,由于设置了存值函数set,任何不符合要求的age属性赋值,都会抛出一个错误,这是数据验证的一种实现方法。利用set方法,还可以数据绑定,即每当对象发生变化时,会自动更新 DOM。

    我们知道,JavaScript语言中没有私有变量一说,通常的做法是在属性名称前加‘_’表示属性为对象内部私有,但这只是一种通用的约定,在语言的内部并没有限制对“私有属性”的访问。

    但是有了Proxy后就可以有效解决。see code

     1 const handler = {
     2   get (target, key) {
     3     invariant(key, 'get');
     4     return target[key];
     5   },
     6   set (target, key, value) {
     7     invariant(key, 'set');
     8     target[key] = value;
     9     return true;
    10   }
    11 };

    // 这一个函数实现了对私有属性访问权限的控制
    12 function invariant (key, action) { 13 if (key[0] === '_') { 14 throw new Error(`Invalid attempt to ${action} private "${key}" property`); 15 } 16 } 17 const target = {}; 18 const proxy = new Proxy(target, handler); 19 proxy._prop 20 // Error: Invalid attempt to get private "_prop" property 21 proxy._prop = 'c' 22 // Error: Invalid attempt to set private "_prop" property

    上面代码中,只要读写的属性名的第一个字符是下划线,一律抛错,从而达到禁止读写内部属性的目的。

    有一点需要注意:set方法的第四个参数receiver,指的是原始的操作行为所在的那个对象,一般情况下是proxy实例本身

     1 const handler = {
     2   set: function(obj, prop, value, receiver) {
     3     obj[prop] = receiver;
     4   }
     5 };
     6 const proxy = new Proxy({}, handler);
     7 const myObj = {};
     8 Object.setPrototypeOf(myObj, proxy);
     9 
    10 myObj.foo = 'bar';
    11 myObj.foo === myObj // true

    // 稍微注意一下~~~~

    上面代码中,设置myObj.foo属性的值时,myObj并没有foo属性,因此引擎会到myObj的原型链去找foo属性。myObj的原型对象proxy是一个 Proxy 实例,设置它的foo属性会触发set方法。这时,第四个参数receiver就指向原始赋值行为所在的对象myObj

    还有一点需要注意,如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用

     1 const obj = {};
     2 Object.defineProperty(obj, 'foo', {
     3   value: 'bar',
     4   writable: false,
     5 });
     6 
     7 const handler = {
     8   set: function(obj, prop, value, receiver) {
     9     obj[prop] = 'baz';
    10   }
    11 };
    12 
    13 const proxy = new Proxy(obj, handler);
    14 proxy.foo = 'baz';
    15 proxy.foo // "bar"

    上面代码中,obj.foo属性不可写,Proxy 对这个属性的set代理将不会生效。

    贪多嚼不烂,先到这里吧~~~~

  • 相关阅读:
    GUI编程之贪吃蛇
    GUI编程之Swing
    Java学习笔记01
    软件测试之使用jmeter进行压力测试
    GitHub以及Git安装的使用
    Axure RP介绍
    结对编程之四则运算
    随心开始
    JAVA入门之简介
    input之File对象的简单介绍
  • 原文地址:https://www.cnblogs.com/cangqinglang/p/9814933.html
Copyright © 2011-2022 走看看