前言
在Vue中我们都听过数据绑定的说法,在Vue中给Html做数据绑定,只要这个数据发生变化,Html上绑定的数据也会发生变化。数据绑定的关键就是监听这个数据的变化;这篇文章我们就来聊聊通过defineProperty和Proxy来劫持对象的属性添加或修改,从而实现数据的双向绑定。
definePropety
object.defineProperty(obj,prop,desc)方法是Es5提供给我们的,它可以在一个对象上定义一个新的属性,或者修改对象现有的属性,并且返回这个对象。
语法
接受三个参数
object.defineProperty(obj,prop,desc)
- obj:被劫持的对象
- prop:要定义或者修改的属性
- desc:描述被定义或者修改的属性
返回值:传入函数的对象,即第一个参数obj
desc参数详解
描述的属性有两种形式分别有以下几种:
数据描述:
value:属性值,可以是任何有效JavaScript值,默认为undefined。writable: 布尔值,控制属性是否可以通过赋值修改,设置为true可以被重写;设置为false,不能被重写。默认为false。enumerable: 布尔值,控制属性是否可枚举,即通过for in循环或者Object.keys访问,设置为true可以被枚举;设置为false,不能被枚举。默认为false。configurable:布尔值,控制属性是否能改变(除了value,writable)及删除,设置为true可以被删除或可以重新设置特性;设置为false,不能被可以被删除或不可以重新设置特性。默认为false。
存取描述:
get: 属性的getter函数,如果没有getter,则为undefined。执行时传入this,this值取决于调用者。set:属性的setter函数,如果没有setter,则为undefined。赋值时执行,并传入this。
应用
我们知道了definePropety的用法,那就来想一个问题,如何监听数据的变化,让数据变成响应式的,即你数据变化之后我有办法能感应到并且做出回应。我们直接来看下面的代码:数据变化之后带来了函数的调用。
function Reactive() {
var value = null
var reactive = []
Object.defineProperty(this, 'num', {
get() {
console.log('执行了get操作');
return value
},
set(newVal) {
console.log('执行了set操作');
value = newVal
reactive.push({ val: newVal })
fn(newVal)
}
})
this.getReactive = function () {
return reactive
}
}
var react = new Reactive()
react.num = 11
console.log(react.getReactive());
function fn(n) {
console.log(`val 值改为:${n}`);
}
上述代码运行结果如下:
我们通过
definePropety劫持对象监听到了数据的改变并且做出来回应,这就是响应式数据,Vue2中的响应式原理就是用到的此方法。
Html版本
利用数据劫持来操作Html:
<body>
<p id="content">1</p>
<button id="btn">点击+1</button>
<script>
let btn = document.getElementById('btn')
let p = document.getElementById('content')
let obj = {
value: 1
}
let value = 1
Object.defineProperty(obj, 'value', {
get() {
return value
},
set(newVal) {
value = newVal
p.innerHTML = newVal
}
})
btn.addEventListener('click', () => {
// p.innerHTML = Number(p.innerHTML) + 1
obj.value += 1
})
</script>
</body>
这样就实现了数据绑定。
有人会说上面的逻辑两行代码就能搞定你偏偏写这么多;其实不然,代码看是增多了,当我们想要改变Html中的值时,我们只需要修改obj.value += 1就行。当我们封装给小白用时,告诉小白你只需要修改值就行,Html上我已经帮你弄好了;就好像尤雨溪封装好了Vue让你去使用,这是一本万利的事。
proxy
Proxy这个词的原意是代理,可以译为“代理器”。Proxy可以理解成在目标对象前架设了一个“拦截层”,外界对该对象的访问都必须先通过这层拦截,因此提供了一种机制可以对外界的访问进行过滤和改写。
与definePropety的区别
defineProperty只能重定义属性的的读取和设置,proxy可以重定义更多的行为。defineProperty会污染原对象proxy去代理了ob,他会返回一个新的代理对象不会对原对象ob进行改动,而defineproperty是去修改元对象,修改元对象的属性,而proxy只是对元对象进行代理并给出一个新的代理对象。
语法
const p = new Proxy(target, handler)
参数
-
target:要使用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。 -
handler: 也是一个对象,其属性是当执行一个操作时定义代理的行为的函数,也就是自定义的行为。
小结
当proxy出来之后,Vue3紧接着也问世了,vue3 用 proxy 代替了Object.defineProperty()方法,使得代码性能等到了提升。我们从中不由得发出感叹:技术的更新不会停止,始终向着更佳的方式迈进。