Proxy和Object.defineProperty的使用

1.Proxy的定义:

Proxy即名为代理,所谓代理就是你要获取或者操作某个东西,通过这个代理中介帮你去做,而不是直接作用于这个对象上。就像在网上买东西一样,通过某猫,某东平台买,换货也是通过平台帮你去换。

Proxy是一种特殊的对象,它提供了一种机制来拦截和自定义对目标对象的操作。

2.Proxy使用方式:

let p=new Proxy(target,handler)

target:用Proxy包装的目标对象(可以是数组、对象,函数等等)

handler:一个对象,拦截过滤代理操作的函数。

注意:要使Proxy其作用,要针对Proxy实例(也就是第二个参数)进行操作。而不是针对目标对象进行操作。

3.proxy代理方式

(1)get(target,prop,receiver):拦截对象属性的访问

(2)set(target,prop,value,receiver):拦截对象属性的设置,最后返回一个布尔值

(3)apply(target,object,agrments):用于拦截函数的调用,比如proxy()

(4) construct(target,agrments):用于拦截new操作符,比如new proxy(),为了使new操作符在生成的proxy对象上生效。

(5)has(target,prop):拦截例如prop in proxy 的操作,返回一个布尔值

(6)deleteProperty(target,prop):拦截例如 delete proxy[prop]的操作,返回一个布尔值

(7)ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.keys(proxy)、for in循环操作等等,返回一个数组。

(8) defineProperty(target,propkey,propDes):拦截Object.defineProperty(proxy,propkey,propDes)

(9) getOwnPropertyDescriptor(target,prop):拦截Object.getOwnPropertyDescriptor(proxy,propkey)

(10) preventExtensions(target):拦截Object.preventExtensions(proxy)

(11) getPrototypeOf(target):拦截Object.getPrototypeOf(proxy)

(12) isExtensible(target):拦截Object.isExtensible(proxy)

(13) setPrototypeOf(target):拦截Object.setPrototypeOf(proxy)

let target = {  
  name: 'Alice',  
  age: 25  
};  
  
// 处理器对象,定义拦截行为  
let handler = {  
  get(target, propKey, receiver) {  
    console.log(`Reading ${propKey}`);  
    return Reflect.get(...arguments);  
  },  
  set(target, propKey, value, receiver) {  
    console.log(`Writing ${propKey}`);  
    return Reflect.set(...arguments);  
  }  
};  
  
// 创建代理对象  
let proxy = new Proxy(target, handler);  
  
// 读取代理对象的属性  
console.log(proxy.name); // 输出: Reading name, 然后输出: Alice  
  
// 修改代理对象的属性  
proxy.age = 30; // 输出: Writing age  
  
// 检查属性是否存在  
console.log('age' in proxy); // 输出: Reading age, 然后输出: true  

// 删除代理对象的属性  
delete proxy.age; // 输出: Writing age, 但不会真的删除,因为handler中没有定义deleteProperty陷阱

4.使用Object.defineProperty实现简单的数据双向绑定

在vue2.0中,数据的绑定是使用es5中的Object.defineProperty。遍历data函数对象中的每个属性,绑定set,get方法,拦截监听数据的变化。

let obj={
  age:18
}
Object.defineProperty(obj, 'age', { 
  get: function() { 
    console.log('get===1') 
    return this._age 
  },  
  set: function(value) {
    this._age = value*3
  }  
});  
obj.age=9 //会触发set方法
console.log(obj.age);// 会触发get方法
console.log(obj)// 输出:{age:27,_age:27} ,不能直接this.age 会报错,需要新建一个属性名去改变原有属性
<body>
  <div>
    姓名:<input type="text" id="input">
  </div>
  <p id="p"></p>

</body>
<script>
  const input=document.getElementById('input')
  const p=document.getElementById('p')
  const obj={
    name:''
  }
  
  input.addEventListener('input',function(e){
    obj.name=e.target.value
  })
  Object.defineProperty(obj,'name',{
    set:function(value){
    input.value=value
    p.innerHTML=value
    this._name=value
  },
    get:function(){
      return this._name
    }
  })
</script>

4.Object.defineProperty() 对比 Proxy 有什么区别?

1.Object.defineProperty()无法一次性监听对象所有属性,必须遍历或者递归来实现。而Proxy的实现就不需要了。

let target = {  
  name: 'Alice',  
  age: 25  
}; 
Object.keys(target).forEach(key => {
  Object.defineProperty(target,key,{
    set:function(value){
     this[`_`+key]=value
    },
    get:function(){
      
      return this[`_`+key]
    }
  })
});

target.age=30
target.name='wufang'
target.name

2.Object.defineProperty()无法监听新增的属性,需要手动再添加新的属性监听。而Proxy则可以监听新增属性。vue2中动态监听新增的属性用Vue.set(obj,key,value)方法。

let girl = {  
  name: 'Alice',  
  age: 25  
}; 
Object.keys(girl).forEach(key => {
  Object.defineProperty(girl,key,{
    set:function(value){
      console.log(value)
     
    },
    get:function(){
      
    }
  })
});

girl.age=30 //会触发set方法
girl.hobby='打篮球' //不会触发set方法

//手动再添加新的属性监听
Object.defineProperty(girl,'hobby',{
   set:function(value){
      console.log(value)
     
    },
    get:function(){
      
    }
})
girl.hobby='打篮球' //会触发set方法
let girl = {  
  name: 'Alice',  
  age: 25  
}; 
let proxy = new Proxy(girl, {  
  get(target, propKey, receiver) {  
    console.log(`Reading ${propKey}`);  
    return Reflect.get(...arguments);  
  },  
  set(target, propKey, value, receiver) {  
    console.log(`Writing ${propKey}`);  
    return Reflect.set(...arguments);  
  }  
});
proxy.hobby='打篮球' //会触发set方法

3.Object.defineProperty 可以监听数组的变化,但是无法对数组的变化响应。但Proxy则可以


let arr=[1,2,3]
arr.forEach((item,index)=>{
  Object.defineProperty(arr,`${index}`,{
   set:function(value){
      
     
    },
    get:function(){
      
    }
 })
})
arr[0]=9 //会触发set方法
arr[4]=19 //不会触发set方法
let arr=[1,2,3]
let proxy = new Proxy(arr, {  
  get(target, propKey, receiver) {  
    console.log(`Reading ${propKey}`);  
    return Reflect.get(...arguments);  
  },  
  set(target, propKey, value, receiver) {  
    console.log(`Writing ${propKey}`);  
    return Reflect.set(...arguments);  
  }  
});
proxy[0]=9 //会触发set方法
proxy[4]=19 //会触发set方法

4.Proxy的应用

表单的校验


const nameEl=document.getElementById('name')
const passwordEl=document.getElementById('password')
let rules={
  name:{
    validate(value){
      return value.length>6
    },
    message:'用户名长度不能小于6位'
  },
  password:{
    validate(value){
      return value.length>10
    },
    message:'密码长度不能小于10位'
  }
}

function validators(obj,rules){
  return new Proxy(obj,{
    set(target,key,value){
      let validator=rules[key]
      if(!validator){
        return target[key]
      }else if(validator.validate(value)){
        return target[key]
      }else{
        alert(validator.message)
      }
    },
    get(){}
  })
}
let form={}
 form=validators(form,rules)
 nameEl.addEventListener('blur',function(e){
  form.name=e.target.value
 })
 passwordEl.addEventListener('blur',function(e){
  form.password=e.target.value
 })

原文链接:https://juejin.cn/post/7342743955315900466 作者:奶茶不加冰七分甜

(0)
上一篇 2024年3月6日 上午11:08
下一篇 2024年3月6日 下午4:00

相关推荐

发表回复

登录后才能评论