js css html

Proxy-Reflect

2022-10-06  本文已影响0人  未路过

1 监听对象的操作

1. 使用Object.defineProperty来监听

var obj = {
  name : 'why',
  age : 18,
}

Object.defineProperty(obj, 'age', {

  get(){
    console.log('getter ');
    return this.age
  },
  set(newValue){
    console.log('setter ' + newValue);
    this.age = newValue;
  },
})
console.log(obj.age);
//test1.html:60 Uncaught RangeError: Maximum call stack size exceeded
//存取属性描述符里面的模拟一个访问和设置的默认行为的时候
//必须的在里面新添一个属性不然会造成循环引用(狂call布置)
//就是不能再get函数里面直接写return this.age,

//方法1: 给obj添加一个_age属性
//方法2:把obj.age放到一个变量里面



/* obj.age --> get.call(obj)---> return this.age--->obj.age
obj.age --> get.call(obj) --->this._age */

2.循环引用的解决方法1

var obj = {
  name : 'why',
  age : 18,
}

Object.defineProperty(obj, 'age', {

  get(){
    console.log('getter ');
    return this._age
  },
  set(newValue){
    console.log('setter ' + newValue);
    this._age = newValue;
  },
})
console.log(obj.age);
//getter
//undefined

obj.age = 19;
console.log(obj.age);
//setter 19
//getter
//19



//obj.age --> get.call(obj) --->this._age 
console.log(obj.hasOwnProperty("age")); //true
console.log(obj.hasOwnProperty("_age")); //true 只有在设置完一次obj.age=18之后obj才有_age这个属性,这个时候才是true,否则一次都没设置过的话是false

3.循环引用的解决方法2

  var obj = {
        name: "why",
        age: 18,
      };

      let myage = obj.age;

      Object.defineProperty(obj, "age", {
        get() {
          console.log("getter");
          return myage;
        },
        set(newValue) {
          console.log("setter");
          myage = newValue;
        },
      });

      console.log(obj.age); //18
      obj.age = 30;
      console.log(obj.age); //30

4.监视对象的所有属性的变化

var obj = {
  name : 'why',
  age : 18,

}

Object.keys(obj).forEach(key => {
  let value = obj[key];

  Object.defineProperty(obj, key, {
    get: function(){
      console.log(`监听到obj对象的${key}属性被访问了`)
      return value
    },
    set: function(newValue) {
      console.log(`监听到obj对象的${key}属性被设置值`)
      value = newValue
    }
  })
})

console.log(obj.name);
obj.name = "kobe"

console.log(obj.age);
obj.age = 19

5.Object.defineProperty来监听对象属性变化的缺点

75.PNG
 var obj = {
        name: "why", // 数据属性描述符
        age: 18, // 数据属性描述符
      };
      console.log(Object.getOwnPropertyDescriptor(obj, "age"));
      // {value: 18, writable: true, enumerable: true, configurable: true}

      Object.defineProperty(obj, "age", {
        get() {
          console.log("getter ");
          return this._age;
        },
        set(newValue) {
          console.log("setter " + newValue);
          this._age = newValue;
        },
      });
      console.log(Object.getOwnPropertyDescriptor(obj, "age"));
      //{enumerable: true, configurable: true, get: ƒ, set: ƒ}
      //name个age就是数据属性,但是要监听它的变化的话,他们就变成了存取属性了。破坏了definePropery的原来的目的了。

2 Proxy

1. Proxy的基本使用

76.PNG
  var obj = {
    name : 'why',
    age : 18
  };

  const proxyObj = new Proxy(obj, {});
  /* 
  
  1.先创建一个proxy对象,这个proxy对象就是obj对象进行代理
  2.第二个参数{}里面是捕获器,里面可以捕获对代理对象的各种操作

  */

  console.log(proxyObj.name);//why
  console.log(proxyObj.age);//18

  proxyObj.name = "coder";
  proxyObj.age = 30;

  console.log(obj.name);//coder
  console.log(obj.age);//30
  //通过修改代理对象,把原来的对象也会修改掉
  //捕获器不重写的情况下,他会自动完成对原来对象的操作
  //通过代理对象设置值,直接设置到原来的对象里面
77.PNG
const obj = {
  name: "why",
  age: 18
}

const objProxy = new Proxy(obj, {
  // 获取值时的捕获器,获取的时候的自动回调
  get: function(target, key) {
    //target指的就是objProxy代理的元对象obj
    console.log(`监听到对象的${key}属性被访问了`, target)
    return target[key]
  },

  // 设置值时的捕获器
  set: function(target, key, newValue) {
    console.log(`监听到对象的${key}属性被设置值`, target)
    target[key] = newValue//因为target是代理的元对象obj,所以就会直接给元对象设置值。
  }
})

console.log(objProxy.name)
console.log(objProxy.age)

objProxy.name = "kobe"
objProxy.age = 30

console.log(obj.name)// "kobe"
console.log(obj.age)//30

2. Proxy的其他捕获器

const obj = {
  name: "why", // 数据属性描述符
  age: 18
}

// 变成一个访问属性描述符
// Object.defineProperty(obj, "name", {

// })

const objProxy = new Proxy(obj, {
  // 获取值时的捕获器
  get: function(target, key) {
    console.log(`监听到对象的${key}属性被访问了`, target)
    return target[key]
  },

  // 设置值时的捕获器
  set: function(target, key, newValue) {
    console.log(`监听到对象的${key}属性被设置值`, target)
    target[key] = newValue
  },

  // 监听in的捕获器
  has: function(target, key) {
    console.log(`监听到对象的${key}属性in操作`, target)
    return key in target
  },

  // 监听delete的捕获器
  deleteProperty: function(target, key) {
    console.log(`监听到对象的${key}属性in操作`, target)
    delete target[key]
  }
})


// in操作符
// console.log("name" in objProxy)

// delete操作
delete objProxy.name

使用defineProperty是有改掉原对象obj的属性操纵符号的,我们不应该随便去改掉他,obj的name本来是一个数据属性操作符,然后就变成了访问属性描述符了。使用Proxy的话是对代理对象进行各种各样的操作。

3.Proxy所有捕获器

78.PNG

在这里,handler.apply()和handle.construct()是用于函数对象

我们使用(杠杠proto)去那对象的原型是不太好的,有浏览器的限制。我们可以用Object.getPrototypeOf去获取对象的原型。

Object.setPrototypeOf(obj, prototype)他是将prototype作为已知对象obj的原型

Object.create(prototype)是创建一个以prototype为原型的对象

4.Proxy对函数对象的监听

我们还会看到捕捉器中还有construct和apply,它们是应用于函数对象的:

function foo() {

}

const fooProxy = new Proxy(foo, {
  apply: function(target, thisArg, argArray) {
    console.log("对foo函数进行了apply调用")
    return target.apply(thisArg, argArray)
  },
  construct: function(target, argArray, newTarget) {
    console.log("对foo函数进行了new调用")
    return new target(...argArray)
  }
})

fooProxy.apply({}, ["abc", "cba"])
new fooProxy("abc", "cba")

/* 
对foo函数进行了apply调用
对foo函数进行了new调用
*/

3 reflect

1.Reflect的作用

79.PNG

Proxy是一个类,使用的时候要new,Reflect就是一个对象,可以直接使用。

我们如何对对象本身做操作比如defineProperty, getPrototypeOf。一开始不知道放在那里,就全部放在Object的静态函数里面。Object.defineProperty =XXX之类的这么定义出来。但是Object是一个构造函数,是用来new 对象的。不符合规范,所以再es6中,新增了reflect这个对象,我们可以使用它来对对象本身做一些操作。把以前的不规范化变的规范化。

比较 Reflect 和 Object 方法

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/Comparing_Reflect_and_Object_methods

2.Reflect的常见方法

Proxy上面有哪些捕获器,Reflect上面就有对应的方法。

80.PNG

3.Reflect和Proxy一起使用

Proxy的目的就是为了不对原来的对象做直接的操作,所以赋值的时候,是对代理对象proxyObj.age = 30 进行赋值,获取值的时候也是通过代理对象来获取属性console.log(proxyObj.age).但是现在事与愿违,现在通过对代理对象进行内部实现的时候,在捕获器里面,还是直接对里面的原来的对象进行操作,比如set里面的target[key] = newvalue等等。所以我们现在可以通过Reflect对元对象进行操作,而非直接对元对象进行操作。

注意;proxy的handler里面只有set和get有receiver参数。

它的作用是什么呢?

如果我们的源对象(obj)有setter、getter的访问器属性,那么可以通过receiver来改变里面的this;

  var obj = {
    name : 'why',
    age : 18
  };

  const objProxy = new Proxy(obj, {
    get: function(target, key, reveiver){
      console.log('get---------');
      // return target[key]
      return Reflect.get(target, key)
    },
    set: function(target, key, newvalue,reveiver){
      console.log('set----------');
      //target[key] = newValue;
      Reflect.set(target, key, newvalue)
    },
    has: function(target, key){
      console.log('has----------');
      return Reflect.has(target, key)
    },
     deleteProperty: function(target, key){
      console.log('delete----------');
      return Reflect.deleteProperty(target, key)
    },
  })

  console.log(objProxy.age);
  objProxy.age = 30;
  console.log(objProxy.age);//30
  console.log(obj.age);//30


  /* 
  在这里
  Reflect.set(target, key, newvalue)
  和
  target[key] = newValue
  这两种方式在某些情况下是有区别的
  (使用Object.freeze()方法可以冻结一个对象,对冻结的对象不可以再添加属性以及删除属性,不可以修改属性的值,但还可以访问到)
  1.target[key] = newValue 真的新值有没有被设置进去是不知道的
  Object.freeze(target)设置之后,对象被冻结,也不能设置
  比如Object.freeze(obj)
  然后objProxy.age=100
  这个时候设置值是不成功的,设置100以后,再次获取,结果还是以前的值30.这个时候使用Reflect的话,我们是知道有没有设置成功的。
  2.Reflect.set(target, key, newvalue)会返回一个布尔类型的值
  成功true,失败false,
        set: function (target, key, newvalue, reveiver) {
          console.log("set----------");
          //target[key] = newValue;
          const result = Reflect.set(target, key, newValue);
          if (result) {
          } else {
          }
        },
  通过if else可以选择接下来设置成功怎么操作,设置失败怎么操作之类的。
  */

4. Receiver的作用

我们发现在使用getter、setter的时候有一个receiver的参数,它的作用是什么呢?

如果我们的源对象(obj)有setter、getter的访问器属性,那么可以通过receiver来改变里面的this;

//每一个对象都有一个get和set方法。
const obj = {
  _name : 'why',//默认是私有属性
  get name() {
    return this._name
  },
  set name(newvalue) {
    this._name = newvalue
  }
}
  console.log(obj.hasOwnProperty("name")); //true
  console.log(obj.hasOwnProperty("_name")); //true

  console.log(Object.getOwnPropertyDescriptor(obj, "name"));
      //{enumerable: true, configurable: true, get: ƒ, set: ƒ}

//obj.name = "coder"//.name调用的是set name()
//console.log(obj.name);//.name调用的是get name()

/* const objProxy = new Proxy(obj, {
  get: function(target, key){
    console.log("get方法被访问--------", key)
    return Reflect.get(target, key)

  },
  set: function(target, key, newValue){
    Reflect.set(target, key, newValue)
  }
}) */

 //console.log(objProxy.name)
//get方法被访问-------- name
//只被访问了1次


 //调用捕获器里面get方法,调用Reflect.get(target, key)
 //因为key是name,这个时候会访问obj里面的get方法
 //然后又通过this._name来访问obj里面的_name属性(直接obj._name, 如果是objProxy._name的化,就可以通过proxy里面的handler来获取访问过程了。)
 /* 
 问题:访问_name,这个已经绕过了proxy了,直接通过obj
 如果我们希望对这个对象的所有属性操作,包括_name,
 是经过代理,这就没办法实现了。比如我们想拦截_name属性的访问,设置之类的。

 解决:让obj里面的return this._name这个this变成代理对象,而不是obj对象

 */


 const objProxy = new Proxy(obj, {
   //这里receiver就是代理对象objProxy
  get: function(target, key, receiver){
    console.log(objProxy === receiver);//true
    console.log("get方法被访问--------", key, receiver)
    return Reflect.get(target, key, receiver)
    //receiver--》改变元对象的get方法里面的this
    /* 
          objProxy.name -> 进入get函数
          然后要Reflect.get(target, key, receiver);receiver--》改变元对象的get方法里面的this
          就等于是return target._name变成了objProxy._name,这个时候就再次来到捕获器的get方法
          再次Reflect.get(target, key, receiver);就获取target._name,因为_name属性不是存取属性,没有this,所以传入的receiver没有作用。
          总结:get就会被访问两次
          
          */

  },
  set: function(target, key, newValue, receiver){
    console.log("set方法被访问--------", key)
    Reflect.set(target, key, newValue, receiver)//这里面receiver的作用是改变set函数中的this指向
  }//在new Proxy(obj, handler)的handler里面只有get set的时候,回调的参数里面才有receiver。
})

console.log(objProxy.name)
//get方法被访问-------- name Proxy {_name: 'why'}
//get方法被访问-------- _name Proxy {_name: 'why'}


objProxy.name = "coderrrr"
//set方法被访问-------- name
//set方法被访问-------- _name

5.Reflect中construct作用

Reflect.construct(target, argumentsList[, newTarget])

对构造函数进行 new 操作,相当于执行 new target(...args)。

function Student(name, age){
  this.name = name;
  this.age = age
}

function Teacher(){

}

const stu = new Student("why", 18);
console.log(stu);//Student类型
console.log(stu.__proto__ === Student.prototype);//true Student类型
console.log(Object.getPrototypeOf(stu) === Student.prototype);//true Student类型

/* 

我们希望new出来的stu的执行Student函数中的内容,
 但是创建出来对象是Teacher对象
*/
//方法1:
const testStu =  new Student("why", 18);
testStu.__proto__ = Teacher.prototype;
console.log(testStu);////Student {name: 'why', age: 18}
console.log(testStu.__proto__ === Teacher.prototype)//true

//方法二:
//指向Student这个构造函数,但是创建出来的类是Teacher类的对象
const teacher = Reflect.construct(Student, ["why", 18], Teacher)
console.log(teacher)//Teacher {name: 'why', age: 18}
console.log(teacher.__proto__ === Teacher.prototype)//true
上一篇 下一篇

猜你喜欢

热点阅读