proxy与Object.defineProperty

82 阅读3分钟

前言

偶然之间打开了vue官网,在首页发现了官方出了一个源码解析的文档分支,就学习了一波,并打算动手敲敲,写个文章加深印象

proxy

遇事不决,先copy一下官网的前两句
proxy修改的是程序默认形为,就形同于在编程语言层面上做修改,属于元编程(meta programming) 元编程(英语:Metaprogramming,又译超编程,是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)作为它们的数据,或者在运行时完成部分本应在编译时完成的工作

proxy参数

  1. target
    targer是需要proxy代理的目标对象,可以是任何类型的对象
  2. handler
    handler是一个通常以函数作为属性的对象,其中函数是用来定义代理对象的行为。

handler对象的方法

其中handler对象的方法有不少,主要就学习了当前接触比较多的getset方法

  1. get方法主要有三个参数,obj、p、receiver。
    obj是目标对象,p是读取的属性,receiver通常是proxy本身。
const obj = {
    name: 'xiaochen',
};

const proxy = new Proxy(obj, {
    get: (obj, p, receiver) => {
        // 模板对象
        console.log(obj);
        // 读取的属性
        console.log(p);
        // proxy本身
        console.log(receiver);
    },
});
console.log(procy.name);
  1. set方法主要有四个参数,obj、p、newValue、receiver
    obj是目标对象,p是读取的属性,newValue是新设置的值,receiver通常是proxy本身。 set方法是返回值,代表设置新的值成功,如果返回false会抛异常
const obj = {
    name: 'xiaochen',
};

const proxy = new Proxy(obj, {
    set: (obj, p, newValue, receiver) => {
        console.log(obj);
        console.log(p);
        console.log(newValue);
        console.log(receiver);
        return true;
    },
});
procy.name = '我是新的值';

proxy实现双向绑定

<template>
    <div class="form">
        <input type="text" @input="changeValue" />
        <div id="text"></div>
    </div>
</template>
<script setup>
let obj = {
    name: '我自有一剑开天门',
};

let proxy = new Proxy(obj, {
    set: (target, p, newValue, receiver) => {
        console.log(target);
        console.log(p);
        console.log(newValue);
        console.log(receiver);
        updateDom(newValue);
        return true;
    },
});

// 更新Dom
function updateDom(objData: { name: string }) {
    document.getElementById('text').innerHTML = objData;
}

function changeValue(e) {
    proxy.name = e.target.value;
}

<script>

Object.defineProperty

Object.defineProperty()  方法可以用来修改对象的属性或者是新增属性并返回此对象。
该方法主要用三个参数,分别为obj、prop、descriptor

# obj为目标对象
# prop为需要操作(新增或者修改)的属性
# descriptor为属性描述符(属性描述符又可以分为数据描述符、存取描述符)
Object.defineProperty(obj, prop, descriptor)

属性描述符

configurable 属性

configurable 特性表示对象的属性是否可以被删除,以及除 value 和 writable 特性外的其他特性是否可以被修改,默认为false。

enumerable

configurable用来控制对象属性是否可以被枚举(我本来想写遍历,但是网上都是用枚举描述),所以就是用枚举,默认为false。

value

value 该属性的值,默认为undefined

writable

writable 上面的value值是否可修改,当该属性为false时,value值不可修改,默认为false

get

get 方法在对象属性被读取时调用,如果加了return,那么return的值会作为返回值,默认为undefined。

set

set 方法在对象属性被修改时调用,该方法接收一个参数,该参数为新的值,默认为undefined。

数据描述符和存取描述符

其中configurableenumerablevaluewritable被称为数据描述符,而get方法和set方法被称为存取描述符,当使用getset方法时,不能使用value和writable,不然会抛异常。

Object.defineProperty实现双向绑定

<template>
    <div class="form">
        <input type="text" @input="changeValue" />
        <div id="text"></div>
    </div>
</template>
<script setup>
let obj = {
    name: '我是一个有趣的对象',
};

Object.defineProperty(obj, 'name', {
    enumerable: true,
    get() {
        console.log('糟糕,我被劫持了,救我');
        return '我被读取了';
    },
    set(val) {
        // 我在同步更新Dom
        console.log(`我是新的值${val}`);
        updateDom(val);
    },
});
// 更新Dom
function updateDom(objData: { name: string }) {
    document.getElementById('text').innerHTML = objData;
}

function changeValue(e) {
    obj.name = e.target.value;
    console.log(obj.name);
}

<script>

总结

这样看来vue2实现双向绑定时,大致是get方法收集依赖,set方法更新视图,其中configurableenumerable都为true。