代理拦截--defineProrerty/proxy

一、defineProperty

1、ES5提供了 Object.defineProperty 方法,该方法可以在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。该方法无法监听数组的变化。

2、Object.defineProperty(obj,prop,descriptor)

  • obj:目标对象
  • prop:需要定义或修改的属性名
  • descriptor:目标属性所拥有的的特性
let obj = { test:"hello"}
//对象已有的属性添加特性描述
Object.defineProperty(obj,"test",{
	//能不能被删除
	configurable: true | false,
	//是否可枚举
	enumerable: true |false,
	value: 任意类型的值,
	//是否可写,此配置和get/set不能同时存在
	writable: true | false,
	get(){},
	set(value){}
})

3、案例:实现数据的双向绑定

<body>
  <div id="root">
    <input type="text" v-model="username">
    <hr>
    <h3 v-bind='username'></h3>
    <input type="text" v-bind="username">
  </div>

  <script>
    // 数据源
    let data = {
      username: '',
      age: 0
    };

    observer(data);

    // 监听数据
    function observer(data) {
      for (let key in data) {
        // 数据代理
        definedReactive(data, key, data[key]);
      }
    }

    // 数据代理
    function definedReactive(target, key, value) {
      Object.defineProperty(target, key, {
        get() {
          return value;
        },
        set(newValue) {
          if (value !== newValue) { //数据发生了改变,才设置
            value = newValue
            // 更新视图
            updateView(key, value)
          }
        }
      })
    }

    // 更新视图
    function updateView(key, value) {
      document.querySelectorAll(`[v-bind='${key}']`).forEach(el => {
        if (el.tagName === 'INPUT') {
          el.value = value
        } else {
          el.innerHTML = value
        }
      })
    }

    // 给input添加键盘事件
    document.querySelectorAll('[v-model]').forEach(el => {
      el.addEventListener('keyup', function () {
        let key = this.getAttribute('v-model');
        data[key] = this.value.trim();
      })
    })

  </script>

</body>

4、案例:实现数据源多层级绑定

    let data = Object.freeze({
      username: '',
      age: 0,
      user: { id: 1 }
    });

    observer(data);

    data.user.id = 2000

    // 监听数据
    function observer(data) {
      // 如果对冻结,则不去处理代理
      if (Object.isFrozen(data)) return data;

      if (typeof data !== 'object') return;
      for (let key in data) {
        // 数据代理
        definedReactive(data, key, data[key]);
      }
    }

    // 数据代理
    function definedReactive(target, key, value) {
      // 递归
      observer(value)

      Object.defineProperty(target, key, {
        get() {
          return value;
        },
        set(newValue) {
          console.log('更新一下视图')
        }
      })
    }

5、案例:es5类中添加获取器和修改器

es5中不存在获取器和修改器,这里可以使用Object.defineProperty来达到类似的效果。

const idKey = Symbol('id');
function Fn(){
 this[idKey] = 100;
 Object.defineProperty(this,'id',{
   get(){
     return this[idKey];
   },
   set(val){
     this[idKey] = val;
   }
 })
}

let fn = new Fn;
fn.id = 1000;
console.log(fn.id);

二、Proxy

1、Proxy是es6提供的新的API,Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

Proxy 这个词的原意是代理,用在这里表示用它来“代理”某些操作,可以理解外“代理器”。

2、Proxy的特点:

  • Proxy 可以直接监听对象而非属性
  • Proxy 可以劫持整个对象,并返回一个新对象,不论是操作便利程度还是底层功能上都远胜于 Object.defineProterty
  • Proxy 可以直接监听数组的变化
  • Proxy 有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has 等等是Object.property 不具备的
let data = {
  id: 100
};

// js以后会把所有的方法,都写在此类中,也会把Object中的方法向此类中移植
let proxy = new Proxy(data, {
  // 获取器
  get(target, key) {
    // return target[key];
    return Reflect.get(target, key) || '';
  },
  // 修改器
  set(target, key, value) {
    // console.log(target, key, value)
    // target[key] = value
    // 如果你开启了严格模式,则此处一定要设置返回值,true
    // return true;
    return Reflect.set(target, key, value);
  }
})

proxy.id = 200;
console.log(proxy.id)

3、案例:字符串截取

<body>

  <div id="root"></div>

  <script>
    const root = document.querySelector('#root');
    const news = [
      {
        title: "海外网深一度:气候危机“逼近灾难临界值”,中国行动振奋世界"
      },
      {
        title: "小区不准外卖员入内,杭州一外卖小哥回家被保安拦下!民警到场调解,小哥情绪崩溃"
      },
      {
        title: "网传浙江慈溪上林中学一女生教室内被多次扇耳光 当地回应:已去处置"
      }
    ];
    let newsProxy = makeProxyTitle(news)

    function makeProxyTitle(data, len = 10) {
      return new Proxy(data, {
        get(target, key) {
          let value = Reflect.get(target, key)
          if (typeof value === "object") {
            value['title'] = value.title.length > len ? value.title.substr(0, len) + '...' : value.title;
            return value;
          } else {
            return value;
          }
        },
        set(target, key, value) {
          if (value !== target[key]) {
            return Reflect.set(target, key, value)
          }
        }
      })
    }


    newsProxy.forEach(item => {
      const li = document.createElement('li');
      // let title = item.title.length > 10 ? item.title.substr(0, 10) + '...' : item.title
      li.innerHTML = item.title;

      root.appendChild(li)
    })

  </script>
</body>

4、案例:proxy双向绑定

<body>
  <div id="root">
    <input type="text" v-model="username">
    <hr>
    <h3 v-bind='username'></h3>
    <input type="text" v-bind="username">
  </div>


  <script>
    // 数据源
    let data = {
      username: ''
    };

    // 监听
    let proxyData = observer(data)

    function observer(data) {
      return new Proxy(data, {
        get(target, key) {
          return Reflect.get(target, key)
        },
        set(target, key, value) {
          if (value !== target[key]) {
            // 更新视图
            updateView(key, value)
            return Reflect.set(target, key, value)
          }
        }
      })
    }

    function updateView(key, value) {
      document.querySelectorAll(`[v-bind='${key}']`).forEach(el => {
        if (el.tagName === 'INPUT') {
          el.value = value
        } else {
          el.innerHTML = value
        }
      })
    }

    // 给input添加键盘事件
    document.querySelectorAll('[v-model]').forEach(el => {
      el.addEventListener('keyup', function () {
        let key = this.getAttribute('v-model');
        proxyData[key] = this.value.trim();
      })
    })

  </script>
</body>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值