Js从definePropety到Es6中的proxy解析Vue中的数据绑定

428 阅读4分钟

前言

Vue中我们都听过数据绑定的说法,在Vue中给Html做数据绑定,只要这个数据发生变化,Html上绑定的数据也会发生变化。数据绑定的关键就是监听这个数据的变化;这篇文章我们就来聊聊通过definePropertyProxy来劫持对象的属性添加或修改,从而实现数据的双向绑定。

definePropety

object.defineProperty(obj,prop,desc)方法是Es5提供给我们的,它可以在一个对象上定义一个新的属性,或者修改对象现有的属性,并且返回这个对象。

语法

接受三个参数

object.defineProperty(obj,prop,desc)

  • obj:被劫持的对象
  • prop:要定义或者修改的属性
  • desc:描述被定义或者修改的属性

返回值:传入函数的对象,即第一个参数obj

desc参数详解

描述的属性有两种形式分别有以下几种:

数据描述:

  1. value:属性值,可以是任何有效JavaScript值,默认为undefined
  2. writable: 布尔值,控制属性是否可以通过赋值修改,设置为true可以被重写;设置为false,不能被重写。默认为false
  3. enumerable: 布尔值,控制属性是否可枚举,即通过for in循环或者Object.keys访问,设置为true可以被枚举;设置为false,不能被枚举。默认为false
  4. configurable:布尔值,控制属性是否能改变(除了value,writable)及删除,设置为true可以被删除或可以重新设置特性;设置为false,不能被可以被删除或不可以重新设置特性。默认为false

存取描述:

  1. get: 属性的 getter 函数,如果没有 getter,则为 undefined。执行时传入this,this值取决于调用者。
  2. 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}`);
}

上述代码运行结果如下:

2.png 我们通过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的区别

  1. defineProperty只能重定义属性的的读取和设置,proxy可以重定义更多的行为。
  2. defineProperty会污染原对象proxy去代理了ob,他会返回一个新的代理对象不会对原对象ob进行改动,而defineproperty是去修改元对象,修改元对象的属性,而proxy只是对元对象进行代理并给出一个新的代理对象。

语法

const p = new Proxy(target, handler)

参数

  • target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

  • handler: 也是一个对象,其属性是当执行一个操作时定义代理的行为的函数,也就是自定义的行为。

小结

proxy出来之后,Vue3紧接着也问世了,vue3proxy 代替了Object.defineProperty()方法,使得代码性能等到了提升。我们从中不由得发出感叹:技术的更新不会停止,始终向着更佳的方式迈进。