对象属性拦截Object.defineProperty()介绍及使用


前言

       一旦数据发生变化,我们可以立刻知道,并且做一些你想完成的事情,这些事情包括但不限于以下:

发送一个网络请求
打印一段文字
操作一个dom


提示:以下是本篇文章正文内容,下面案例可供参考

对象属性拦截

一、传统写法

用字面量定义对象设置值如下代码:

	// 字面量定义对象
    let obj = {
      name: '晚星'
    }
    console.log(obj.name);
    obj.name = '晚星good'

再浏览器中访问obj.name,如下图:
在这里插入图片描述

缺点:不管是访问还是修改 我们开发者无从得知什么时候进行的修改
// 没有插入我们自定义动作的机会

二、拦截式写法

如下代码:

	let data = {}
    Object.defineProperty(data, 'name', {
      // 再访问data中name的属性的时候会自动调用
      get() {
        console.log('data中的name属性被访问了');
      },
      // 在修改data中name属性的时候会自动调用
      set() {
        console.log('data中的name属性被设置了');
      }
    })

在浏览器中设置data.name的值如下图:
在这里插入图片描述

在拦截器中Object.defineProperty
get 方法主要监视该对象是是否又能调用,如果调用则执行函数体的代码
set方法 主要是对象里面的属性或者方法被调用后,再执行函数体里面的代码

三、拦截器中get与set

get方法如果没有return 返回值,则返回undefined

set方法中如果设置形参,再打印的时候会自动获取设置的形参打印

如下代码:

	let data = {}
    Object.defineProperty(data, 'name', {
      // 当我们访问data.name的时候 实际上拿到的就是get函数的返回值
      get() {
        console.log('data中的name属性被访问了');
        return '晚星good'
      },
      // 当我们对data.name进行修改的时候  新值会被自动当成实参传入到newValue的位置
      set(newValue) {
        console.log('data中的name属性被设置了,新值为:' + newValue);

      }
    })

显示效果如下:
在这里插入图片描述
三、对象中的联动

1.字面量对象

       obj.name设置了一个初始值,当我调用它的时候,它显示它的初始值,当我设置了它的obj.name的值后,当我再次调用obj.name它显示的是我修改之后的值,这就是对象中联动。

代码如下:

	let obj = {
      name: '晚星'
    }
    console.log(obj.name);
    obj.name = '晚星'

如下图:
在这里插入图片描述

2.拦截器对象

       obj.name而在拦截器对象中,当我设置完data.name的值后,再调用data.name的时候,它显示的还是初始值。细心的小伙伴可以先想想这是为什么?

	let data = {}
    let _name = '晚星'
    Object.defineProperty(data, 'name', {
      get() {
        console.log('data中的name属性被访问了');
        return '晚星'
      },
      set(newValue) {
        console.log('data中的name属性被设置了,新值为:' + newValue);
      }
    })

问题出现再了get方法中的return,因为它始终返回的都是return里面的值。

针对这一问题解决方案:

// 1.get函数返回的始终是一个固定值
// 2.set函数接收到的新值之后没有进行任何操作
// 解决方案:借助一个两个函数中都可以访问到的中间变量去做一个数据的中转
// 最终目的:get函数return出去的 其实已经变成set函数传下来的新值

代码如下:

	let data = {}
    let _name = '晚星'
    Object.defineProperty(data, 'name', {
      get() {
        console.log('data中的name属性被访问了');
        return _name
      },
      set(newValue) {
        console.log('data中的name属性被设置了,新值为:' + newValue);
        _name = newValue
      }
    })

如下图:
在这里插入图片描述
3.更加通用的属性拦截的方案优化

       data.name首先给大家回忆下Object.keys(data)它是遍历对象的键,如果有一个已经很很多属性的对象,让你们把所有的键和值都打印出来你们会怎么做?

示例代码如下:

  let data = {
    name: "晚星",
    age: 23,
    height: 180
  }
  Object.keys(data).forEach(key => {
    console.log(key + ' : ' + data[key]);//打印出所有的键和值
  })

       data.name但是如果只是获取到键和值的话,还是无法改变data对象中的属性,所以这时候就要结合属性拦截,来获取与修改对象data的值。

我们的需求是:
1.针对于已存在多个属性的对象进行get/set处理
2.每个属性的get和set都是可以进行联动的

代码如下:

  Object.keys(data).forEach(key => {
    // 针对于每一个属性都进行响应式转换
    defineReactive(data, key, data[key])
  })

  // 专门用来进行响应式转换
  function defineReactive(data, key, value) {
    Object.defineProperty(data, key, {
      get() {
        return value
      },
      set(newValue) {
        value = newValue
      }
    })
  }

演示效果如图下:
在这里插入图片描述
原理: 核心:闭包 + 作用域
在这里插入图片描述

  1. 每一次遍历 都会执行defineReactive函数 每次执行都会形成一块独立的函数作用域
  2. get和set函数使我们defineReactive函数内部的函数 在内部函数中引用外部函数内的变量 可以形成闭包,由于value变量被内部函数引用,defineReactive执行完毕之后,value变量一致不被销毁
  3. 由于value一直不被销毁 且可以被get函数和set函数基于作用域访问到,所以value一直可以作为get和set联动中间变量

四、数据变化反应到视图

       在上面我们讲了怎么改变已经定义的对象里面的属性,但是我们只是再控制台查看的效果,接下来,我们来说下,怎么把数据或者更改的数据显示在视图上,方便我们直观的查看数据的变化过程。

目标:
p元素的内部可以显示name属性的值 并且一旦name属性发生变化 p标签内的内容也会得到更新

如下图:

在这里插入图片描述

实现思路
1.要想控制元素内容的显示 离不开dom操作 dom.innerText
2.响应式已经实现好了 只需要在set函数中 重新使用最新的值 交给dom元素使用即可

实现代码如下:

   let data = {
      name: '晚星'
    }
    
    Object.keys(data).forEach(key => {
      defineReactive(data, key, data[key])
    })
    
    function defineReactive(data, key, value) {
      Object.defineProperty(data, key, {
        get() {
          return value
        },
        set(newValue) {
          value = newValue
          console.log('name属性发生变化了 最新的值为:', newValue);
          // 使用data的name数据  再一次的渲染视图
          const p = document.querySelector('#app p').innerText = data.name
        }
      })
    }
    //获取dom元素
    const p = document.querySelector('#app p').innerText = data.name

总结

1. 不管是指令也好,插值表达式也好,这些都是将数据反应到视图的标记而已,通过标记我们可以把数据的变化响应式的反应到对应的dom位置上去
2. 找标记,把数据绑定到dom的过程,我们称之为bindin

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值