数据代理学习

Object.defineProperty

Object.defineProperty的基本使用

作用:给一个对象添加属性

参数:接受三个参数:

  • 给哪个对象添加属性
  • 添加的属性名
  • 配置项
    let person = {
      name: '张三',
      sex: '男',
    };
    
    Object.defineProperty(person, 'age', {
      value: '18'
    })
    console.log(person);

在这里插入图片描述
此时,会发现新添加age属性的颜色与person本身的属性颜色不同,会稍微淡一些,这是表示新添加的age属性是不可以被枚举的,也就是说不可以被遍历。

验证:使用Object.keys()来遍历,Object.keys可以将传入对象里面所有属性的属性名提取出来变为一个数组。

console.log(Object.keys(person));

在这里插入图片描述
使用for… in… 也是同样的结果(既能遍历数组也能遍历对象)

for (let key in person){
    //输出person对象每一个属性的属性值
	console.log(person[key]);
}

在这里插入图片描述
那么如何能够让使用Object.defineProperty添加的属性也能遍历呢?只需要在添加时加上enumerable属性即可

  • enumberable: 控制属性是否可以枚举;默认值是false
  • writable:控制属性是否可以被修改,默认值为fasle
  • configurable:控制属性是否可以被删除,默认值为false

Object.defineProperty的应用

先有一需求,有一个对象person,和一个变量number,现在需要给这个对象person添加属性age,属性值为number

方法一:直接添加
    let number = 18;
    let person = {
      name: '张三',
      sex: '男',
    };

    person.age = number;
    console.log(person);

这样当然可以,但是当我们修改number后,age并不会随之而改变
在这里插入图片描述
除非再次给赋值: person.age = number;
那么如何不赋值就能让person的age随之number的改变而改变呢?这时就需要使用Object.defineProperty了

方法二:使用Object.defineProperty

Object.defineProperty的配置项可以写一个配置set和get

get:当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值

    let number = 18;
    let person = {
      name: '张三',
      sex: '男',
    };
    Object.defineProperty(person, 'age', {
      get: function () {
        return number;
      }
    })
    console.log(person);

此时,输出person,会发现age属性被折叠起来了,并且鼠标悬浮会有‘Invoke property getter ’的提示,意思为age属性的值由getter映射而来。并且,这个对象身上还有一个为age服务的getter
在这里插入图片描述
此时,修改number后,age也会随之而改变;因为当每一次访问age,都会触发getter的调用
在这里插入图片描述
set:当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值

    let number = 18;
    let person = {
      name: '张三',
      sex: '男',
    };

    // person.age = number;
    // console.log(person);

    Object.defineProperty(person, 'age', {
      get: function () {
        console.log('有人读取age属性了!')
        return number;
      },
      set(value) {
        console.log('有人修改了age属性,且值是' + value);
      }
    })
    console.log(person);

此时,修改person.age = 19时,会发现,person的age属性并没有被改变,仍然是18
在这里插入图片描述
这是因为person的age属性依赖于number,number并没有被改变,因此person.age也就没有被改变,所以set应该写为

set(value) {
  console.log('有人修改了age属性,且值是' + value);
  number = value;
}

这个时候number和person.age相互影响,修改一个,另一个也会随之更改。

数据代理

定义:通过一个对象代理另一个对象中属性的操作(读/写)就叫数据代理

为什么要使用数据代理

之所以使用代理,就是不希望用户能够直接访问某个对象,直接操作对象的某个成员(因为这样是不可控的,我们不知道用户在访问操作哪一个对象)

通过代理,我们可以拦截用户的访问(称为数据劫持),拦截住后我们就可以对数据进行一些处理,比如做一些数据的验证或者像Vue一样做一些视图更新的额外操作,之后再允许用户的访问操作(因为我们拦截了用户的每一次访问,这样用户操作对象就完全是在我们可控的范围内)

理解数据代理

有两个对象obj1,obj2;现在想要让对象obj2也访问到对象obj1中的属性x,并且可以修改这个属性x;这就是数据代理,通过obj2去代理obj1对象的中属性,并对其进行操作。

代码实现:

    let obj1 = { x: 100};
    let obj2 = { y: 200};

    Object.defineProperty(obj2, 'x', {
      get() {
        return obj1.x;
      },
      set(value) {
        obj1.x = value;
      }
    })
    console.log(obj2);

在这里插入图片描述当修改了obj2身上的x,obj1身上的x也会随之改变

在这里插入图片描述

vue中的数据代理

<body>
  <div id="root">
    <h2>学校名称:{{name}}</h2>
    <h2>学校地址:{{address}}</h2>
  </div>
  <script type="text/javascript">
    const vm = new Vue({
      el: '#root',
      data: {
        name: '北京大学',
        address: '北京',
      }
    })
    console.log(vm);
  </script>
</body>

将Vue实例vm输出:
在这里插入图片描述
会发现data中的变量会在实例vm上,当鼠标移入data中的变量时,会有‘Invoke property getter ’的提示,因此,实例vm上属于data中的变量实际上是由Object.defineProperty()方法定义上去的。

也就是说当访问实例vm身上的name时,getter工作,将某个地方的name取出来给vm,当修改实例vm身上name时,setter工作,将某个地方的name也修改掉;这个地方就是data,修改的也就是data里的name。

在这里插入图片描述

验证两条线

getter

修改代码中data的名称,vm身上的data随之而改变
在这里插入图片描述
在这里插入图片描述

setter

修改vm身上的name
在这里插入图片描述
此时,data中的name是多少呢?在判断data中的name时,首先要拿到data中的name。是通过data.name吗?
在这里插入图片描述
显然不是,data只是配置对象中的一个属性,并不是一个变量。那么该如何拿到data中的name呢?

我们知道new Vue时传入的配置对象都是为vm实例所服务的,那么vm实例必然会将这些配置的属性存下来,而vm在存data时则使用_data这个属性名,将其存在vm自身。验证一下:

在这里插入图片描述
setter这条线验证完毕

在这里插入图片描述

数据代理的目的就是为使编码更方便,否则,页面中的插值语法里写的不是{{ name }},而是{{ _data.name }}

总结:

Vue中的数据代理
通过vm对象来代理data对象中属性的操作(读/写)
Vue中数据代理的好处
更加方便的操作data中的数据
基本原理
通过Object.defineProperty()把data对象中所有属性添加到vm上。为每一个添加到vm上的属性,都指定一个getter/setter。在getter/setter内部去操作(读/写)data中对应的属性。

补充

在这里插入图片描述

当我们输出vm时,会发现_data中的属性也有getter和setter方法,这不是数据代理,而是数据劫持;使用getter和setter方法去修改_data中的属性是为了实现响应式
在这里插入图片描述
想要实现红色的线,就必须让Vue可以监测到data中的name,而数据劫持就是为了做这个功能的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值