ES6学习笔记——代理与反射

12-1.属性描述符(回顾)

Property Descriptor 属性描述符 是一个普通对象,用于描述一个属性的相关信息;

通过Object.getOwnPropertyDescriptor(对象, 属性名)可以得到一个对象的某个属性的属性描述符;

也可以通过 Object.getOwnPropertyDescriptors(对象)可以得到某个对象的所有的属性描述符;

  • value:属性值
  • configurable:该属性的描述符是否可以修改(默认值为true)
  • enumerable:该属性是否可以被枚举(默认值为true)
  • writable:该属性(的值)是否可以被重新赋值(默认值为true)

如果需要为某个对象添加属性时 或 修改属性时, 配置其属性描述符,可以使用下面的代码:

Object.defineProperty(对象, 属性名, 描述符);

Object.defineProperties(对象, 多个属性的描述符)

 const obj = {
            a: 1,
            b: 2
        }
        //obj.a = 3;
       Object.defineProperty(obj, "a", {
            value: 3,
            configurable: false,  //之后就不能在针对该属性的属性描述符进行修改
           enumerable: false,    // 之后该属性就不可枚举,(会影响for in 循环,Object.keys得到一个对象的所有属性名,Object.value得到一个对象的所有属性值)
            writable: false     // 之后该属性不能被重新修改赋值
       })
     //  Object.defineProperties(obj, {
     //       a: {
     //           value: 3,
     //           configurable: false,
     //           enumerable: false,
     //           writable: false
     //       }
     //   })
     obj.a = 10; 
     console.log(obj); // {b: 2, a: 3}
存取器属性

属性描述符中,如果配置了 get 和 set 中的任何一个,则该属性,不再是一个普通属性,而变成了存取器属性。

get 和 set配置均为函数,如果一个属性是存取器属性,则读取该属性时,会运行get方法,将get方法得到的返回值作为属性值;如果给该属性赋值,则会运行set方法

存取器属性最大的意义,在于可以控制属性的读取和赋值。

const obj = {
            b: 2
        }
        Object.defineProperty(obj, "a", {
            get() {
                console.log("运行了属性a的get函数")
            },
            set(val){
                console.log("运行了属性a的set函数", val) 
            }
        })
        obj.a = 20 + 10;     // set(20+10),   运行了属性a的set函数", 30
        console.log(obj.a);  // console.log(get()) , 运行了属性a的get函数  返回 undefined

		// obj.a = obj.a + 1;   // set(obj.a + 1)   set(get() + 1)   运行了属性a的set函数", NaN
        // console.log(obj.a);  // undefined
const obj = {
            b: 2
        }
        Object.defineProperty(obj, "a", {
            get() {
                console.log("运行了属性a的get函数")
                return obj._a;
            },
            set(val){
                console.log("运行了属性a的set函数", val)
                obj._a = val;
            }
        })
        obj.a = 10;                //  运行了属性a的set函数 10
        console.log(obj.a);        // 运行了属性a的get函数
                                   // 10  
	obj = {
            name: "adsf"
      	  }

        Object.defineProperty(obj, "age", {
            get() {
                return obj._age;
            },
            set(val) {
                if (typeof val !== "number") {
                    throw new TypeError("年龄必须是一个数字")
                }
                if (val < 0) {
                    val = 0;
                } else if (val > 200) {
                    val = 200;
                }
                obj._age = val;
            }
        })
        obj.age = "Asdfasasdf";
        console.log(obj.age);   //报错  TypeError: 年龄必须是一个数字  at Object.set

实例:

 <p>
        <span>姓名:</span>
        <span id="name"></span>
    </p>
    <p>
        <span>年龄:</span>
        <span id="age"></span>
    </p>
		const spanName = document.getElementById("name")
        const spanAge = document.getElementById("age")

        const user = {}

        Object.defineProperties(user, {
            name: {
                get() {
                    return spanName.innerText;
                },
                set(val) {
                    spanName.innerText = val;
                }
            },
            age: {
                get() {
                    return +spanAge.innerText;
                },
                set(val) {
                    if (typeof val !== "number") {
                        throw new TypeError("年龄必须是一个数字")
                    }
                    if (val < 0) {
                        val = 0;
                    } else if (val > 200) {
                        val = 200;
                    }
                    spanAge.innerText = val;
                }
            }
        })

12-2.Reflect 反射

  1. Reflect是什么?

    Reflect是一个内置的JS对象,它提供了一系列方法,可以让开发者通过调用这些方法,访问一些JS底层功能

    由于它类似于其他语言的反射,因此取名为Reflect

  2. 它可以做什么?

    使用Reflect可以实现诸如 属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在与对象中 等等功能

  3. 这些功能不是已经存在了吗?为什么还需要用Reflect实现一次?

    有一个重要的理念,在ES5就被提出:减少魔法、让代码更加纯粹

    这种理念很大程度上是受到函数式编程的影响

    ES6进一步贯彻了这种理念,它认为,对属性内存的控制、原型链的修改、函数的调用等等,这些都属于底层实现,属于一种魔法,因此,需要将它们提取出来,形成一个正常的API,并高度聚合到某个对象中,于是,就造就了Reflect对象

    因此,你可以看到Reflect对象中有很多的API都可以使用过去的某种语法或其他API实现。

  4. 它里面到底提供了哪些API呢?

  • Reflect.set(target, propertyKey, value): 设置对象target的属性propertyKey的值为value,等同于给对象的属性赋值
		const obj = {
            a: 1,
            b: 2
        }
        // obj.a = 10;  //  魔法
        Reflect.set(obj, "a", 10);  // 使用API 给obj的a属性赋值为10
  • Reflect.get(target, propertyKey): 读取对象target的属性propertyKey,等同于读取对象的属性值
const obj = {
            a: 1,
            b: 2
        }
        // obj.a = 10;
        Reflect.set(obj, "a", 10);
        console.log(Reflect.get(obj, "a"));  // 使用API 读取obj的a属性的值
  • Reflect.apply(target, thisArgument, argumentsList):调用一个指定的函数,并绑定this参数列表。等同于函数调用
	function method(a, b){
            console.log("method", a, b);
     }
        // method(3, 4);  // 魔法
    Reflect.apply(method, null, [3, 4]);
  • Reflect.deleteProperty(target, propertyKey):删除一个对象的属性
	const obj = {
            a: 1,
            b: 2
        }
        // delete obj.a;   // 魔法
        Reflect.deleteProperty(obj, "a");  // 删除obj对象的a属性
  • Reflect.defineProperty(target, propertyKey, attributes):类似于Object.defineProperty(对象, 属性名, 描述符)为某个对象添加属性时或修改属性时, 配置其属性描述符;不同的是如果配置出现问题,返回false而不是报错
	const obj = {
            a: 1,
            b: 2
        }
        //obj.a = 3;
       Reflect.defineProperty(obj, "a", {
            value: 3,
            configurable: false,  //之后就不能在针对该属性的属性描述符进行修改
           enumerable: false,    // 之后该属性就不可枚举,(会影响for in 循环,Object.keys得到一个对象的所有属性名,Object.value得到一个对象的所有属性值)
            writable: false     // 之后该属性不能被重新修改赋值
       })
  • Reflect.construct(target, argumentsList):用构造函数的方式创建一个对象
	function Test(a, b) {
            this.a = a;
            this.b = b;
        }
        // const t = new Test(1, 3);  //  魔法
        const t = Reflect.construct(Test, [1, 3]); //构造函数的方式创建一个对象
        console.log(t)
  • Reflect.has(target, propertyKey): 判断一个对象是否拥有一个属性
	const obj = {
            a: 1,
            b: 2
        }
        // console.log("a" in obj);  //  魔法
        console.log(Reflect.has(obj, "a"));  //判断obj里是否有a属性    true

12-3.Proxy 代理

代理:提供了修改底层实现的方式

Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

//代理一个目标对象
//target:目标对象
//handler:是一个普通对象,其中可以重写底层实现
//返回一个代理对象

const proxy = new Proxy(target, handler);

Proxy对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为如果handler没有设置任何拦截,那就等同于直接通向原对象。

const obj = {
            a: 1,
            b: 2
        }

        const proxy = new Proxy(obj, {
            set(target, propertyKey, value) {
                // console.log(target, propertyKey, value);
                // target[propertyKey] = value;
                Reflect.set(target, propertyKey, value);
            },
            get(target, propertyKey) {
                if (Reflect.has(target, propertyKey)) {
                    return Reflect.get(target, propertyKey);
                } else {
                    return -1;
                }
            },
            has(target, propertyKey) {
                return false;
            }
        });
        // console.log(proxy);
        // proxy.a = 10;
        // console.log(proxy.a);

        console.log(proxy.d);  // -1
        console.log("a" in proxy);  //  false  在代理那里做了修改,全部返回了false

部分内容可以参考阮一峰老师的ES6 教程

12-4 应用—观察者模式

有一个对象,是观察者,它用于观察另外一个对象的属性值变化,当属性值变化后会收到一个通知,可能会做一些事。

 		//创建一个观察者
        function observer(target) {
            const div = document.getElementById("container");
          //  const ob = {};
          // const props = Object.keys(target); // 得到所有的属性名
          // for (const prop of props) {
          //    Object.defineProperty(ob, prop, {
          //       get() {
          //           return target[prop];
          //       },
          //       set(val) {
          //           target[prop] = val;
          //           render();
          //       },
          //       enumerable: true
          //   })
          //}
          	const proxy = new Proxy(target, {
                set(target, prop, value) {
                    Reflect.set(target, prop, value);
                    render();
                },
                get(target, prop){
                    return Reflect.get(target, prop);
                }
            })
            render();

            function render() {
                let html = "";
                for (const prop of Object.keys(ob)) {
                    html += `
                        <p><span>${prop}:</span><span>${ob[prop]}</span></p>
                    `;
                }
                div.innerHTML = html;
            }

            return proxy;
        }
        const target = {
            a: 1,
            b: 2
        }
        const obj = observer(target)
    </script>

应用—偷懒的构造函数

	 class User {

      		  }
        function ConstructorProxy(Class, ...propNames) {
            return new Proxy(Class, {
                construct(target, argumentsList) {
                    const obj = Reflect.construct(target, argumentsList)
                    propNames.forEach((name, i) => {
                        obj[name] = argumentsList[i];
                    })
                    return obj;
                }
            })
        }
        const UserProxy = ConstructorProxy(User, "firstName", "lastName", "age")
        const obj = new UserProxy("胖", "虎", 18);
        console.log(obj)


        class Monster {

        }
        const MonsterProxy = ConstructorProxy(Monster, "attack", "defence", "hp", "rate", "name")
        const m = new MonsterProxy(10, 20, 100, 30, "怪物")
        console.log(m);

应用—可验证的函数参数

  function sum(a, b) {
            return a + b;
        }
	function validatorFunction(func, ...types) {
	    const proxy = new Proxy(func, {
	        apply(target, thisArgument, argumentsList) {
	            types.forEach((t, i) => {
	                const arg = argumentsList[i]
	                if (typeof arg !== t) {
	                    throw new TypeError(`第${i+1}个参数${argumentsList[i]}不满足类型${t}`);
	                }
	            })
	            return Reflect.apply(target, thisArgument, argumentsList);
	        }
	    })
	    return proxy;
	}

    const sumProxy = validatorFunction(sum, "number", "number")
    console.log(sumProxy(1, 2))
	function sum(a, b) {
            return a + b;
        }
      function validatorFunction(func, ...types) {
           return function(...argumentsList) {
               types.forEach((t, i) => {
                   const arg = argumentsList[i]
                   if (typeof arg !== t) {
                       throw new TypeError(`第${i+1}个参数${argumentsList[i]}不满足类型${t}`);
                   }
               })
               return func(...argumentsList)
           }
           return proxy;
       }

       const sumProxy = validatorFunction(sum, "number", "number")
       console.log(sumProxy(1, 2))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值