Vue中数据代理详解(从底层刨析)

数据代理

在讨论数据代理之前,大家先要知道它的底层,也就是

Object.defineProperty(给哪个对象添加属性,‘添加的属性名’,{配置项})

这个翻译过来,也就是给对象定义属性,不要小巧它,它在Vue底层中是一个相当重要的角色,不论是这次要说到的数据代理,还是数据劫持,还是Vue中我们熟知的计算属性(computed)等等,都离不开它

Object.defineProperty

我们用代码演示一下,我们给一个person对象添加属性

<script>
        let person = {
            name: 'shaka',
            sex: '男'
        }
        Object.defineProperty(person, 'age', {
            value: 21
        })
        console.log(person);
</script>

在这里插入图片描述
我们毫无疑问会输出这样一个结果,那么,我们会问,为什么不直接在person里面直接写属性呢,我们先来写一下看看

<script>
        let person = {
            name: 'shaka',
            sex: '男',
            age: 21
        }

        console.log(person);
    </script>

在这里插入图片描述
我们有没有发现一个细节问题,这两个图中的age 颜色不一样,我们直接写的age属性,和别的属性是一个颜色,然而,我们在用Object.defineProperty添加的适合,age的颜色,和另外两个颜色不一样,如果没意识到的话,大家翻上去好好看一下。

那么,用Object.defineProperty添加属性,颜色不一样,代表着什么呢?
它的意思是:

用Object.defineProperty,添加的属性,是不可枚举

什么意思呢,简单来说,就是用Object.defineProperty添加的age方法是不可被遍历的

我们来验证一下
1.普通方法添加属性

<body>
    <script>
        let person = {
            name: 'shaka',
            sex: '男',
            age: 21
        }
        // Object.defineProperty(person, 'age', {
        //     value: 21
        // })
        console.log(Object.keys(person));
        console.log(person);
    </script>
</body>

在这里插入图片描述
2.Object.defineProperty添加属性

<body>
    <script>
        let person = {
            name: 'shaka',
            sex: '男',
            // age: 21
        }
        Object.defineProperty(person, 'age', {
            value: 21
        })
        console.log(Object.keys(person));
        console.log(person);
    </script>
</body>

在这里插入图片描述
看出来区别了吧!

那么,如果我们想要遍历它怎么办?这时候,我们要借助一个配置项

enumerable : true

我们尝试一下

<body>
    <script>
        let person = {
            name: 'shaka',
            sex: '男',
            // age: 21
        }
        Object.defineProperty(person, 'age', {
            value: 21,
            enumerable: true,//控制属性是否可以枚举,默认值是false
        })
        console.log(Object.keys(person));
        console.log(person);
    </script>
</body>

在这里插入图片描述

好,这样我们就可以枚举了,但是就是加了这个配置项以后,它还是和正常的属性不同,比如,它是不可修改的
在这里插入图片描述
这时候我们引入另一个配置项

writable:true

<body>
    <script>
        let person = {
            name: 'shaka',
            sex: '男',
            // age: 21
        }
        Object.defineProperty(person, 'age', {
            value: 21,
            enumerable: true,//控制属性是否可以枚举,默认值是false
            writable: true//控制属性是否可以被修改,默认值是false
        })
        console.log(Object.keys(person));
        console.log(person);
    </script>
</body>

在这里插入图片描述

这回它就可以被修改了,现在还有一点,就是这个age属性无法被删除
在这里插入图片描述
这次我们引入下一个配置项

configurable:true

<body>
    <script>
        let person = {
            name: 'shaka',
            sex: '男',
            // age: 21
        }
        Object.defineProperty(person, 'age', {
            value: 21,
            enumerable: true,
            writable: true,
            configurable: true//控制属性是否可以被删除,默认值是false
        })
        console.log(Object.keys(person));
        console.log(person);
    </script>
</body>

在这里插入图片描述
这下,就可以进行删除了,但是Object.defineProperty还有别的配置项,这里我们就说这基本的三种

好,我们开始有新的需求
我们用另一个变量来传这个age的值
传统方式:

    <script>
        let number = 21
        let person = {
            name: 'shaka',
            sex: '男',
            age: number
        }
        console.log(person);
    </script>
</body>

在这里插入图片描述
可以正常实现是吧,但是,这样会有一个问题,我们后期修改age的值,怎么办?
在这里插入图片描述
我们发现改不了,这样,我们就要求助Object.defineProperty了

<body>
    <script>
        let number = 21
        let person = {
            name: 'shaka',
            sex: '男'
        }
        Object.defineProperty(person, 'age', {
            //当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
            get() {
                return number
            }
        })
        console.log(person);
    </script>
</body>

在这里插入图片描述
在这里插入图片描述
当我们鼠标轻放到age的…的时候会出来这个英文提示
这个 getter的意思就是get这个函数的整体

这样,我们就可以对number进行更改了
在这里插入图片描述
需要注意的是,只要读取属性了,就会调用get函数!

好,既然有get了,那么就会有一个跟它相辅相成的方法:set

<body>
    <script>
        let number = 21
        let person = {
            name: 'shaka',
            sex: '男'
        }
        Object.defineProperty(person, 'age', {
            //当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
            get() {
                return number
            },
            //当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
            set(value) {
                console.log('有人修改了age属性,且值是', value)
                number = value
            }

        })
        console.log(person);
    </script>
</body>

可以自己输出试一下,这里我就不说了

什么是数据代理

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

好,概念我们知道了,我们用一个例子比较直观地看一下

我们有两个对象,obj1,obj2,我们通过obj2来改变obj1里面的值

	<body>
		<!-- 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)-->
		<script type="text/javascript" >
			let obj = {x:100}
			let obj2 = {y:200}

			Object.defineProperty(obj2,'x',{
				get(){
					return obj.x
				},
				set(value){
					obj.x = value
				}
			})
		</script>
	</body>

ok,这就是数据代理了,也可以说数据代理是做什么的了

Vue中的数据代理

我们先写一段简单的代码,相信大家都可以看懂

<body>

    <!-- 准备好一个容器-->
    <div id="root">
        <h2>名字:{{name}}</h2>
        <h2>地址:{{address}}</h2>
    </div>
</body>

<script type="text/javascript">
    Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

    const vm = new Vue({
        el: '#root',
        data: {
            name: 'shaka',
            address: '辽宁东北'
        }
    })
    console.log(vm);
</script>

在这里插入图片描述
我们发现Vue实例上就有了address,name这两个属性,我们思考一件很关键的事情,这两个属性,是怎么加上去的
是不是就是Object.defineProperty,发挥了作用,一旦调用了的话,是不是Object.defineProperty中的get方法开始起作用了,
当我们给属性赋值的时候,是不是就是调用了Object.defineProperty中的set方法呢
思考一下这个问题,跟上面的知识串联一下,就明白了
在这里插入图片描述
我们读取name,getter就会工作,就会把data.name读取出去
在这里插入图片描述

如果有人去改这个name,setter就会工作,就会把data.name给改掉
在这里插入图片描述
说了这么多,这个数据绑定究竟有什么作用呢?

Vue中数据代理的好处:更加方便的操作data中的数据

什么意思呢?就是说,我咋没看出来哪里方便了呢?
那么我再引出Vue中数据代理的原理:

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

还是不明白?那么我带着大家演示一下,没有这个数据代理,我们会是什么情况

<body>
    <!-- 
    1.Vue中的数据代理:
    通过vm对象来代理data对象中属性的操作(读/写)
    2.Vue中数据代理的好处:
    更加方便的操作data中的数据
    3.基本原理:
    通过Object.defineProperty()把data对象中所有属性添加到vm上。
    为每一个添加到vm上的属性,都指定一个getter/setter。
    在getter/setter内部去操作(读/写)data中对应的属性。
    -->
    <!-- 准备好一个容器-->
    <div id="root">
        <h2>名字:{{_data.name}}</h2>
        <h2>地址:{{_data.address}}</h2>
    </div>
</body>

<script type="text/javascript">
    Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

    const vm = new Vue({
        el: '#root',
        data: {
            name: 'shaka',
            address: '辽宁东北'
        }
    })
    console.log(vm);
</script>

看出区别了吗?
在这里插入图片描述
这里的书写是不是不一样了?

为什么没有数据绑定会这样呢?那么我反问大家,为什么你会在这个插值里面直接写name和address呢?
你看我直接调data上的这两个属性有意义吗
在这里插入图片描述
也就是说,我们无法直接调用data里面的值
但是Vue实例中给我们定义了一个 _data 的属性

在这里插入图片描述

我们可以通过vm._data.name或者vm.name来调用
在这里插入图片描述
但是如果我们有数据代理的话: 通过Object.defineProperty()把data对象中所有属性添加到vm上。也就是我们看到的name和address
每次我们调用或者修改的时候,都会通过Object.defineProperty中的getter和setter
所以我们就在插值里直接写name和address就行了,所以,Vue中数据代理的好处:

更加方便的操作data中的数据

  • 10
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林多多@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值