前言

大家好,我是林三心,大家也晓得,本菜鸟平时写根底文章比拟多,我始终深信两句话

  • 用最通俗易懂的话,讲最难的知识点
  • 根底是进阶的前提
    其实Vue3曾经进去很久了,可能大部分公司都用上了,然而,Vue3到底比Vue2好在哪里?其实很多人都不晓得。明天我就先给大家讲一讲Vue3的响应式原理吧,顺便说一说Vue3的响应式到底比Vue2的响应式好在哪

好在哪?

好的,咱们先来讲讲为什么Vue3的响应式 优于 Vue2响应式。可能平时问大家:请问你晓得Vue的响应式是怎么实现的吗?大家都能粗略答复进去

  • Vue2的响应式是基于Object.defineProperty实现的
  • Vue3的响应式是基于ES6的Proxy来实现的

是的,尽管下面的答复形象了点,然而的确是答复出了Vue的两个版本的响应式的外围原理,并且Vue的两个版本响应式的好坏,也的确就是体现在Object.definePropertyProxy的差别上。

Vue2

大家都晓得Vue2的响应式是基于Object.defineProperty的,那我就拿Object.defineProperty来举个例子

// 响应式函数function reactive(obj, key, value) {  Object.defineProperty(data, key, {    get() {      console.log(`拜访了${key}属性`)      return value    },    set(val) {      console.log(`将${key}由->${value}->设置成->${val}`)      if (value !== val) {        value = val      }    }  })}const data = {  name: '林三心',  age: 22}Object.keys(data).forEach(key => reactive(data, key, data[key]))console.log(data.name)// 拜访了name属性// 林三心data.name = 'sunshine_lin' // 将name由->林三心->设置成->sunshine_linconsole.log(data.name)// 拜访了name属性// sunshine_lin

通过下面的例子,我想大家都对Object.defineProperty有了一个理解,那问题来了?它到底有什么弊病呢?使得尤大大在Vue3中摈弃了它,咱们接着看:

// 接着下面代码data.hobby = '打篮球'console.log(data.hobby) // 打篮球data.hobby = '打游戏'console.log(data.hobby) // 打游戏

这下大家能够看出Object.defineProperty有什么弊病了吧?咱们能够看到,data新增了hobby属性,进行拜访和设值,然而都不会触发get和set,所以弊病就是:Object.defineProperty只对初始对象里的属性有监听作用,而对新增的属性有效。这也是为什么Vue2中对象新增属性的批改须要应用Vue.$set来设值的起因。

Vue3

从下面,咱们晓得了Object.defineProperty的弊病,咱们接着讲Vue3中响应式原理的外围Proxy是怎么补救这一缺点的,老样子,咱们还是举例子(先粗略讲,具体参数上面会细讲):

const data = {  name: '林三心',  age: 22}function reactive(target) {  const handler = {    get(target, key, receiver) {      console.log(`拜访了${key}属性`)      return Reflect.get(target, key, receiver)    },    set(target, key, value, receiver) {      console.log(`将${key}由->${target[key]}->设置成->${value}`)      Reflect.set(target, key, value, receiver)    }  }  return new Proxy(target, handler)}const proxyData = reactive(data)console.log(proxyData.name)// 拜访了name属性// 林三心proxyData.name = 'sunshine_lin'// 将name由->林三心->设置成->sunshine_linconsole.log(proxyData.name)// 拜访了name属性// sunshine_lin

能够看到,其实成果与下面的Object.defineProperty没什么差异,那为什么尤大大要摈弃它,抉择Proxy呢?留神了,最最最要害的来了,那就是对象新增属性,来看看成果吧:

proxyData.hobby = '打篮球'console.log(proxyData.hobby)// 拜访了hobby属性// 打篮球proxyData.hobby = '打游戏'// 将hobby由->打篮球->设置成->打游戏console.log(proxyData.hobby)// 拜访了hobby属性// 打游戏

所以当初大家晓得Vue3的响应式比Vue2好在哪了吧?

Vue3响应式原理

说完Proxy的益处,咱们正式来讲讲Vue3的响应式原理的外围局部吧。

前言

先看看上面这段代码

let name = '林三心', age = 22, money = 20let myself = `${name}往年${age}岁,贷款${money}元`console.log(myself) // 林三心往年22岁,贷款20元money = 300// 预期:林三心往年22岁,贷款300元console.log(myself) // 理论:林三心往年22岁,贷款20元

大家想一下,我想要让myself跟着money变,怎么办才行?嘿嘿,其实,只有让myself = '${name}往年${age}岁,贷款${money}元'再执行一次就行,如下

let name = '林三心', age = 22, money = 20let myself = `${name}往年${age}岁,贷款${money}元`console.log(myself) // 林三心往年22岁,贷款20元money = 300myself = `${name}往年${age}岁,贷款${money}元` // 再执行一次// 预期:林三心往年22岁,贷款300元console.log(myself) // 理论:林三心往年22岁,贷款300元

effect

下面说了,每一次money扭转就得再执行一次myself = '${name}往年${age}岁,贷款${money}元',能力使myself更新,其实这么写不优雅,咱们能够封装一个effect函数

let name = '林三心', age = 22, money = 20let myself = ''const effect = () => myself = `${name}往年${age}岁,贷款${money}元`effect() // 先执行一次console.log(myself) // 林三心往年22岁,贷款20元money = 300effect() // 再执行一次console.log(myself) // 林三心往年22岁,贷款300元

其实这样也是有害处的,不信你能够看看上面这种状况

let name = '林三心', age = 22, money = 20let myself = '', ohtherMyself = ''const effect1 = () => myself = `${name}往年${age}岁,贷款${money}元`const effect2 = () => ohtherMyself = `${age}岁的${name}竟然有${money}元`effect1() // 先执行一次effect2() // 先执行一次console.log(myself) // 林三心往年22岁,贷款20元console.log(ohtherMyself) // 22岁的林三心竟然有20元money = 300effect1() // 再执行一次effect2() // 再执行一次console.log(myself) // 林三心往年22岁,贷款300元console.log(ohtherMyself) // 22岁的林三心竟然有300元

减少了一个ohtherMyself,就得再写一个effect,而后每次更新都执行一次,那如果减少数量变多了,那岂不是每次都要写好多好多的effect函数执行代码?

track和trigger

针对下面的问题,咱们能够这样解决:用track函数把所有依赖于money变量effect函数都收集起来,放在dep里,dep为什么用Set呢?因为Set能够主动去重。收集起来之后,当前只有money变量一扭转,就执行trigger函数告诉dep里所有依赖money变量effect函数执行,实现依赖变量的更新。先来看看代码吧,而后我再通过一张图给大家展现一下,怕大家头晕哈哈。

let name = '林三心', age = 22, money = 20let myself = '', ohtherMyself = ''const effect1 = () => myself = `${name}往年${age}岁,贷款${money}元`const effect2 = () => ohtherMyself = `${age}岁的${name}竟然有${money}元`const dep = new Set()function track () {    dep.add(effect1)    dep.add(effect2)}function trigger() {    dep.forEach(effect => effect())}track() //收集依赖effect1() // 先执行一次effect2() // 先执行一次console.log(myself) // 林三心往年22岁,贷款20元console.log(ohtherMyself) // 22岁的林三心竟然有20元money = 300trigger() // 告诉变量myself和otherMyself进行更新console.log(myself) // 林三心往年22岁,贷款300元console.log(ohtherMyself) // 22岁的林三心竟然有300元

对象呢?

下面都是讲根底数据类型的,那咱们来讲讲对象吧,我先举个例子,用最原始的形式去实现他的响应

const person = { name: '林三心', age: 22 }let nameStr1 = ''let nameStr2 = ''let ageStr1 = ''let ageStr2 = ''const effectNameStr1 = () => { nameStr1 = `${person.name}是个大菜鸟` }const effectNameStr2 = () => { nameStr2 = `${person.name}是个小蠢才` }const effectAgeStr1 = () => { ageStr1 = `${person.age}岁曾经算很老了` }const effectAgeStr2 = () => { ageStr2 = `${person.age}岁还算很年老啊` }effectNameStr1()effectNameStr2()effectAgeStr1()effectAgeStr2()console.log(nameStr1, nameStr2, ageStr1, ageStr2)// 林三心是个大菜鸟 林三心是个小蠢才 22岁曾经算很老了 22岁还算很年老啊person.name = 'sunshine_lin'person.age = 18effectNameStr1()effectNameStr2()effectAgeStr1()effectAgeStr2()console.log(nameStr1, nameStr2, ageStr1, ageStr2)// sunshine_lin是个大菜鸟 sunshine_lin是个小蠢才 18岁曾经算很老了 18岁还算很年老啊

下面的代码,咱们也看进去了,感觉写的很无脑。。还记得后面讲的dep收集effect吗?咱们暂且把person对象里的name和age看成两个变量,他们都有各自的依赖变量

  • name:nameStr1和nameStr2
  • age:ageStr1和ageStr2
    所以name和age应该领有本人的dep,并收集各自依赖变量所对应的effect

后面说了dep是应用Set,因为person领有age和name两个属性,所以领有两个dep,那用什么来贮存这两个dep呢?咱们能够用ES6的另一个数据结构Map来贮存

const person = { name: '林三心', age: 22 }let nameStr1 = ''let nameStr2 = ''let ageStr1 = ''let ageStr2 = ''const effectNameStr1 = () => { nameStr1 = `${person.name}是个大菜鸟` }const effectNameStr2 = () => { nameStr2 = `${person.name}是个小蠢才` }const effectAgeStr1 = () => { ageStr1 = `${person.age}岁曾经算很老了` }const effectAgeStr2 = () => { ageStr2 = `${person.age}岁还算很年老啊` }const depsMap = new Map()function track(key) {    let dep = depsMap.get(key)    if (!dep) {        depsMap.set(key, dep = new Set())    }    // 这里先暂且写死    if (key === 'name') {        dep.add(effectNameStr1)        dep.add(effectNameStr2)    } else {        dep.add(effectAgeStr1)        dep.add(effectAgeStr2)    }}function trigger (key) {    const dep = depsMap.get(key)    if (dep) {        dep.forEach(effect => effect())    }}track('name') // 收集person.name的依赖track('age') // 收集person.age的依赖effectNameStr1()effectNameStr2()effectAgeStr1()effectAgeStr2()console.log(nameStr1, nameStr2, ageStr1, ageStr2)// 林三心是个大菜鸟 林三心是个小蠢才 22岁曾经算很老了 22岁还算很年老啊person.name = 'sunshine_lin'person.age = 18trigger('name') // 告诉person.name的依赖变量更新trigger('age') // 告诉person.age的依赖变量更新console.log(nameStr1, nameStr2, ageStr1, ageStr2)// sunshine_lin是个大菜鸟 sunshine_lin是个小蠢才 18岁曾经算很老了 18岁还算很年老啊

下面咱们是只有一个person对象,那如果有多个对象呢?怎么办?咱们都晓得,每个对象会建设一个Map来存储此对象里属性的dep(应用Set来存储),那如果有多个对象,该用什么来存储每个对象对应的Map呢?请看下图

其实ES6还有一个新的数据结构,叫做WeakMap的,咱们就用它来存储这些对象的Map吧。所以咱们得对track函数trigger函数进行革新,先看看之前他们长啥样

const depsMap = new Map()function track(key) {    let dep = depsMap.get(key)    if (!dep) {        depsMap.set(key, dep = new Set())    }    // 这里先暂且写死    if (key === 'name') {        dep.add(effectNameStr1)        dep.add(effectNameStr2)    } else {        dep.add(effectAgeStr1)        dep.add(effectAgeStr2)    }}function trigger (key) {    const dep = depsMap.get(key)    if (dep) {        dep.forEach(effect => effect())    }}

之前的代码只做了单个对象的解决计划,然而当初如果要多个对象,那就得应用WeakMap进行革新了(接下来代码可能有点啰嗦,但都会为了关照基础薄弱的同学)

const person = { name: '林三心', age: 22 }const animal = { type: 'dog', height: 50 }const targetMap = new WeakMap()function track(target, key) {    let depsMap = targetMap.get(target)    if (!depsMap) {        targetMap.set(target, depsMap = new Map())    }    let dep = depsMap.get(key)    if (!dep) {        depsMap.set(key, dep = new Set())    }    // 这里先暂且写死    if (target === person) {        if (key === 'name') {            dep.add(effectNameStr1)            dep.add(effectNameStr2)        } else {            dep.add(effectAgeStr1)            dep.add(effectAgeStr2)        }    } else if (target === animal) {        if (key === 'type') {            dep.add(effectTypeStr1)            dep.add(effectTypeStr2)        } else {            dep.add(effectHeightStr1)            dep.add(effectHeightStr2)        }    }}function trigger(target, key) {    let depsMap = targetMap.get(target)    if (depsMap) {        const dep = depsMap.get(key)        if (dep) {            dep.forEach(effect => effect())        }    }}

通过了下面的革新,咱们终于实现了多对象的依赖收集,咱们来试一试吧

const person = { name: '林三心', age: 22 }const animal = { type: 'dog', height: 50 }let nameStr1 = ''let nameStr2 = ''let ageStr1 = ''let ageStr2 = ''let typeStr1 = ''let typeStr2 = ''let heightStr1 = ''let heightStr2 = ''const effectNameStr1 = () => { nameStr1 = `${person.name}是个大菜鸟` }const effectNameStr2 = () => { nameStr2 = `${person.name}是个小蠢才` }const effectAgeStr1 = () => { ageStr1 = `${person.age}岁曾经算很老了` }const effectAgeStr2 = () => { ageStr2 = `${person.age}岁还算很年老啊` }const effectTypeStr1 = () => { typeStr1 = `${animal.type}是个大菜鸟` }const effectTypeStr2 = () => { typeStr2 = `${animal.type}是个小蠢才` }const effectHeightStr1 = () => { heightStr1 = `${animal.height}曾经算很高了` }const effectHeightStr2 = () => { heightStr2 = `${animal.height}还算很矮啊` }track(person, 'name') // 收集person.name的依赖track(person, 'age') // 收集person.age的依赖track(animal, 'type') // animal.type的依赖track(animal, 'height') // 收集animal.height的依赖effectNameStr1()effectNameStr2()effectAgeStr1()effectAgeStr2()effectTypeStr1()effectTypeStr2()effectHeightStr1()effectHeightStr2()console.log(nameStr1, nameStr2, ageStr1, ageStr2)// 林三心是个大菜鸟 林三心是个小蠢才 22岁曾经算很老了 22岁还算很年老啊console.log(typeStr1, typeStr2, heightStr1, heightStr2)// dog是个大菜鸟 dog是个小蠢才 50曾经算很高了 50还算很矮啊person.name = 'sunshine_lin'person.age = 18animal.type = '猫'animal.height = 20trigger(person, 'name')trigger(person, 'age')trigger(animal, 'type')trigger(animal, 'height')console.log(nameStr1, nameStr2, ageStr1, ageStr2)// sunshine_lin是个大菜鸟 sunshine_lin是个小蠢才 18岁曾经算很老了 18岁还算很年老啊console.log(typeStr1, typeStr2, heightStr1, heightStr2)// 猫是个大菜鸟 猫是个小蠢才 20曾经算很高了 20还算很矮啊

Proxy

通过下面的学习,咱们曾经能够实现当数据更新时,他的依赖变量也跟着扭转,然而还是有毛病的,大家能够发现,每次咱们总是得本人手动去执行track函数进行依赖收集,并且当数据扭转时,我么又得手动执行trigger函数去进行告诉更新

那么,到底有没有方法能够实现,主动收集依赖,以及主动告诉更新呢?答案是有的,Proxy能够为咱们解决这个难题。咱们先写一个reactive函数,大家先照敲,了解好Proxy-track-trigger这三者的关系,前面我会讲为什么这里Proxy须要搭配Reflect

function reactive(target) {    const handler = {        get(target, key, receiver) {            track(receiver, key) // 拜访时收集依赖            return Reflect.get(target, key, receiver)        },        set(target, key, value, receiver) {            Reflect.set(target, key, value, receiver)            trigger(receiver, key) // 设值时主动告诉更新        }    }    return new Proxy(target, handler)}

而后改一改之前的代码,把手动track手动trigger去掉,发现也能实现之前的成果

const person = reactive({ name: '林三心', age: 22 }) // 传入reactiveconst animal = reactive({ type: 'dog', height: 50 }) // 传入reactiveeffectNameStr1()effectNameStr2()effectAgeStr1()effectAgeStr2()effectTypeStr1()effectTypeStr2()effectHeightStr1()effectHeightStr2()console.log(nameStr1, nameStr2, ageStr1, ageStr2)// 林三心是个大菜鸟 林三心是个小蠢才 22岁曾经算很老了 22岁还算很年老啊console.log(typeStr1, typeStr2, heightStr1, heightStr2)// dog是个大菜鸟 dog是个小蠢才 50曾经算很高了 50还算很矮啊person.name = 'sunshine_lin'person.age = 18animal.type = '猫'animal.height = 20console.log(nameStr1, nameStr2, ageStr1, ageStr2)// sunshine_lin是个大菜鸟 sunshine_lin是个小蠢才 18岁曾经算很老了 18岁还算很年老啊console.log(typeStr1, typeStr2, heightStr1, heightStr2)// 猫是个大菜鸟 猫是个小蠢才 20曾经算很高了 20还算很矮啊

可能有的同学会有点懵逼,对下面的代码有点纳闷,也可能有点绕,我还认为通过一张图给大家解说一下流程,图可能会被压缩,倡议点开看看

解决写死问题

在下面有一处中央,咱们是写死的,大家都还记得吗,就是在track函数

function track(target, key) {    let depsMap = targetMap.get(target)    if (!depsMap) {        targetMap.set(target, depsMap = new Map())    }    let dep = depsMap.get(key)    if (!dep) {        depsMap.set(key, dep = new Set())    }    // 这里先暂且写死    if (target === person) {        if (key === 'name') {            dep.add(effectNameStr1)            dep.add(effectNameStr2)        } else {            dep.add(effectAgeStr1)            dep.add(effectAgeStr2)        }    } else if (target === animal) {        if (key === 'type') {            dep.add(effectTypeStr1)            dep.add(effectTypeStr2)        } else {            dep.add(effectHeightStr1)            dep.add(effectHeightStr2)        }    }}

理论开发中,必定是不止两个对象的,如果每多加一个对象,就得多加一个else if判断,那是万万不行的。那咱们要怎么解决这个问题呢?其实说难也不难,Vue3的作者们想出了一个十分奇妙的方法,应用一个全局变量activeEffect来奇妙解决这个问题,具体是怎么解决呢?其实很简略,就是每一个effect函数一执行,就把本身放到对应的dep里,这就能够不须要写死了。

咱们怎么能力实现这个性能呢?咱们须要改装一下effect函数才行,并且要批改track函数

let activeEffect = nullfunction effect(fn) {    activeEffect = fn    activeEffect()    activeEffect = null // 执行后立马变成null}function track(target, key) {    // 如果此时activeEffect为null则不执行上面    // 这里判断是为了防止例如console.log(person.name)而触发track    if (!activeEffect) return    let depsMap = targetMap.get(target)    if (!depsMap) {        targetMap.set(target, depsMap = new Map())    }    let dep = depsMap.get(key)    if (!dep) {        depsMap.set(key, dep = new Set())    }    dep.add(activeEffect) // 把此时的activeEffect增加进去}// 每个effect函数改成这么执行effect(effectNameStr1)effect(effectNameStr2)effect(effectAgeStr1)effect(effectAgeStr2)effect(effectTypeStr1)effect(effectTypeStr2)effect(effectHeightStr1)effect(effectHeightStr2)

实现ref

咱们在Vue3中是这么应用ref

let num = ref(5)console.log(num.value) // 5

而后num就会成为一个响应式的数据,而且应用num时须要这么写num.value能力应用

实现ref其实很简略,咱们下面曾经实现了reactive,只须要这么做就能够实现ref

function ref (initValue) {    return reactive({        value: initValue    })}

咱们能够来试试成果如何

let num = ref(5)effect(() => sum = num.value * 100)console.log(sum) // 500num.value = 10console.log(sum) // 1000

实现computed

咱们顺便简略实现一下computed吧,其实也很简略

function computed(fn) {    const result = ref()    effect(() => result.value = fn()) // 执行computed传入函数    return result}

咱们来看看后果

let num1 = ref(5)let num2 = ref(8)let sum1 = computed(() => num1.value * num2.value)let sum2 = computed(() => sum1.value * 10)console.log(sum1.value) // 40console.log(sum2.value) // 400num1.value = 10console.log(sum1.value) // 80console.log(sum2.value) // 800num2.value = 16console.log(sum1.value) // 160console.log(sum2.value) // 1600

自此咱们就实现了本文章所有性能

最终代码

const targetMap = new WeakMap()function track(target, key) {    // 如果此时activeEffect为null则不执行上面    // 这里判断是为了防止例如console.log(person.name)而触发track    if (!activeEffect) return    let depsMap = targetMap.get(target)    if (!depsMap) {        targetMap.set(target, depsMap = new Map())    }    let dep = depsMap.get(key)    if (!dep) {        depsMap.set(key, dep = new Set())    }    dep.add(activeEffect) // 把此时的activeEffect增加进去}function trigger(target, key) {    let depsMap = targetMap.get(target)    if (depsMap) {        const dep = depsMap.get(key)        if (dep) {            dep.forEach(effect => effect())        }    }}function reactive(target) {    const handler = {        get(target, key, receiver) {            track(receiver, key) // 拜访时收集依赖            return Reflect.get(target, key, receiver)        },        set(target, key, value, receiver) {            Reflect.set(target, key, value, receiver)            trigger(receiver, key) // 设值时主动告诉更新        }    }    return new Proxy(target, handler)}let activeEffect = nullfunction effect(fn) {    activeEffect = fn    activeEffect()    activeEffect = null}function ref(initValue) {    return reactive({        value: initValue    })}function computed(fn) {    const result = ref()    effect(() => result.value = fn())    return result}

Proxy和Reflect

Proxy

const person = { name: '林三心', age: 22 }const proxyPerson = new Proxy(person, {    get(target, key, receiver) {        console.log(target) // 原来的person        console.log(key) // 属性名        console.log(receiver) // 代理后的proxyPerson    },    set(target, key, value, receiver) {        console.log(target) // 原来的person        console.log(key) // 属性名        console.log(value) // 设置的值        console.log(receiver) // 代理后的proxyPerson    }})proxyPerson.name // 拜访属性触发get办法proxyPerson.name = 'sunshine_lin' // 设置属性值触发set办法

Reflect

在这列举Reflect的两个办法

  • get(target, key, receiver):集体了解就是,拜访targetkey属性,然而this是指向receiver,所以理论是拜访的值是receiver的key的值,然而这可不是间接拜访receiver[key]属性,大家要辨别一下
  • set(target, key, value, receiver):集体了解就是,设置targetkey属性为value,然而this是指向receiver,所以理论是是设置receiver的key的值为value,但这可不是间接receiver[key] = value,大家要辨别一下

下面咱们强调了,不能间接receiver[key]或者receiver[key] = value,而是要通过Reflect.get和Reflect.set,绕个弯去拜访属性或者设置属性,这是为啥呢?上面咱们举个反例

const person = { name: '林三心', age: 22 }const proxyPerson = new Proxy(person, {    get(target, key, receiver) {        return Reflect.get(receiver, key) // 相当于 receiver[key]    },    set(target, key, value, receiver) {        Reflect.set(receiver, key, value) // 相当于 receiver[key] = value    }})console.log(proxyPerson.name)proxyPerson.name = 'sunshine_lin' // 会间接报错,栈内存溢出 Maximum call stack size exceeded

为什么会这样呢?看看下图解答

当初晓得为什么不能间接receiver[key]或者receiver[key] = value了吧,因为间接这么操作会导致有限循环,最终报错。所以正确做法是

const person = { name: '林三心', age: 22 }const proxyPerson = new Proxy(person, {    get(target, key, receiver) {        return Reflect.get(target, key, receiver)    },    set(target, key, value, receiver) {        Reflect.set(target, key, value, receiver)    }})console.log(proxyPerson.name) // 林三心proxyPerson.name = 'sunshine_lin'console.log(proxyPerson.name) // sunshine_lin

必定有的同学就要问了,上面这么写也能够,为什么也不倡议呢?我放到上面一起说

const proxyPerson = new Proxy(person, {    get(target, key, receiver) {        return Reflect.get(target, key)    },    set(target, key, value, receiver) {        Reflect.get(target, key, value)    }})

为什么要一起用

其实Proxy不搭配Reflect也是能够的。咱们能够这么写,也照样能实现想要的成果

const person = { name: '林三心', age: 22 }const proxyPerson = new Proxy(person, {    get(target, key, receiver) {        return target[key]    },    set(target, key, value, receiver) {        target[key] = value    }})console.log(proxyPerson.name) // 林三心proxyPerson.name = 'sunshine_lin'console.log(proxyPerson.name) // sunshine_lin

那为什么倡议Proxy和Reflect一起应用呢?因为Proxy和Reflect的办法都是一一对应的,在Proxy里应用Reflect会进步语义化

  • Proxy的get对应Reflect.get
  • Proxy的set对应Reflect.set
  • 还有很多其余办法我就不一一列举,都是一一对应的

还有一个起因就是,尽量把this放在receiver上,而不放在target

为什么要尽量把this放在代理对象receiver上,而不倡议放原对象target上呢?因为原对象target有可能原本也是是另一个代理的代理对象,所以如果this始终放target上的话,出bug的概率会大大提高,所以之前的代码为什么不倡议,大家应该晓得了吧?

const proxyPerson = new Proxy(person, {    get(target, key, receiver) {        return Reflect.get(target, key)    },    set(target, key, value, receiver) {        Reflect.set(target, key, value)    }})

结语

我是林三心,一个热心的前端菜鸟程序员。如果你上进,喜爱前端,想学习前端,那咱们能够交朋友,一起摸鱼哈哈,摸鱼群,加我请备注【思否】