Lin Sanxin drew 8 pictures, the most easy to understand analysis of Vue3 responsive core principle

preface

Hello, I'm Lin Sanxin. As you know, this rookie usually writes more basic articles. I always believe in two sentences

  • Speak the most difficult knowledge in the most easy to understand words
  • Foundation is the premise of advanced
    In fact, Vue3 has been out for a long time and may be used by most companies. However, what is better about Vue3 than Vue2? In fact, many people don't know. Today, I'll first tell you the principle of Vue3's response. By the way, Vue3's response is better than Vue2's response.

What's good?

OK, let's talk about why Vue3's response is better than Vue2's response. May I ask you: do you know how Vue's response is implemented? Everyone can give a rough answer

  • Vue2's response is based on object Implementation of defineproperty
  • Vue3's response is implemented based on ES6's Proxy

Yes, although the above answer is a little abstract, it does answer the core principles of Vue's two versions of responsiveness, and the quality of Vue's two versions of responsiveness is indeed reflected in object The difference between defineproperty and Proxy.

Vue2

As we all know, Vue2's response is based on object Defineproperty, then I'll take object Define property as an example

// Response function
function reactive(obj, key, value) {
  Object.defineProperty(data, key, {
    get() {
      console.log(`Visited ${key}attribute`)
      return value
    },
    set(val) {
      console.log(`take ${key}from->${value}->Set as->${val}`)
      if (value !== val) {
        value = val
      }
    }
  })
}


const data = {
  name: 'Lin Sanxin',
  age: 22
}
Object.keys(data).forEach(key => reactive(data, key, data[key]))
console.log(data.name)
// Accessed the name attribute
// Lin Sanxin
data.name = 'sunshine_lin' // Set name from - > Lin Sanxin - > to - > sunshine_ lin
console.log(data.name)
// Accessed the name attribute
// sunshine_lin

Through the above example, I think everyone is right about object With an understanding of defineproperty, what's the problem? What are its disadvantages? You da abandoned it in Vue3. Let's continue:

// Then the code above

data.hobby = 'Play basketball'
console.log(data.hobby) // Play basketball
data.hobby = 'Play games'
console.log(data.hobby) // Play games

Now you can see that object What's wrong with defineproperty? We can see that data adds the hobby attribute to access and set values, but neither get nor set will be triggered, so the disadvantage is: object Defineproperty only listens to the properties in the initial object, and is invalid for new properties. This is why Vue. Com is used to modify the new attributes of objects in Vue2$ Set to set the reason for the value.

Vue3

From above, we know object For the disadvantages of defineproperty, let's talk about how the core Proxy of the response principle in Vue3 makes up for this defect. As usual, let's give an example (first roughly, the specific parameters will be described in detail below):

const data = {
  name: 'Lin Sanxin',
  age: 22
}

function reactive(target) {
  const handler = {
    get(target, key, receiver) {
      console.log(`Visited ${key}attribute`)
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      console.log(`take ${key}from->${target[key]}->Set as->${value}`)
      Reflect.set(target, key, value, receiver)
    }
  }

  return new Proxy(target, handler)
}

const proxyData = reactive(data)

console.log(proxyData.name)
// Accessed the name attribute
// Lin Sanxin
proxyData.name = 'sunshine_lin'
// Set name from - > Lin Sanxin - > to - > sunshine_ lin
console.log(proxyData.name)
// Accessed the name attribute
// sunshine_lin

You can see that the effect is actually the same as that of the above object Defineproperty is no different, so why should we abandon it and choose Proxy? Note that the most important thing is to add attributes to the object. Let's see the effect:

proxyData.hobby = 'Play basketball'
console.log(proxyData.hobby)
// Accessed the hobby property
// Play basketball
proxyData.hobby = 'Play games'
// Set the hobby from - > playing basketball - > to - > playing games
console.log(proxyData.hobby)
// Accessed the hobby property
// Play games

So now you know what's better about Vue3's response than Vue2?

Vue3 responsive principle

After finishing the benefits of Proxy, let's formally talk about the core of Vue3's responsive principle.

preface

Take a look at the following code first

let name = 'Lin Sanxin', age = 22, money = 20
let myself = `${name}this year ${age}Years old, deposit ${money}element`

console.log(myself) // Lin Sanxin is 22 years old and has a deposit of 20 yuan

money = 300

// Expectation: Lin Sanxin is 22 years old and has a deposit of 300 yuan
console.log(myself) // Actual: Lin Sanxin is 22 years old and has a deposit of 20 yuan

Let's think about it. I want myself to change with money. What can I do? Hey, hey, actually, just let myself = '${name} be ${age} years old this year and deposit ${money} Yuan' again, as shown below

let name = 'Lin Sanxin', age = 22, money = 20
let myself = `${name}this year ${age}Years old, deposit ${money}element`

console.log(myself) // Lin Sanxin is 22 years old and has a deposit of 20 yuan

money = 300

myself = `${name}this year ${age}Years old, deposit ${money}element` // Do it again

// Expectation: Lin Sanxin is 22 years old and has a deposit of 300 yuan
console.log(myself) // Actual: Lin Sanxin is 22 years old and has a deposit of 300 yuan

effect

As mentioned above, every time money changes, we have to execute myself = '${name} this year ${age} years old and deposit ${money} Yuan' to update myself. In fact, it's not elegant to write this. We can encapsulate an effect function

let name = 'Lin Sanxin', age = 22, money = 20
let myself = ''
const effect = () => myself = `${name}this year ${age}Years old, deposit ${money}element`

effect() // Execute once first
console.log(myself) // Lin Sanxin is 22 years old and has a deposit of 20 yuan
money = 300

effect() // Do it again

console.log(myself) // Lin Sanxin is 22 years old and has a deposit of 300 yuan

In fact, this is also harmful. If you don't believe it, you can take a look at the following situation

let name = 'Lin Sanxin', age = 22, money = 20
let myself = '', ohtherMyself = ''
const effect1 = () => myself = `${name}this year ${age}Years old, deposit ${money}element`
const effect2 = () => ohtherMyself = `${age}Year old ${name}Actually ${money}element`

effect1() // Execute once first
effect2() // Execute once first
console.log(myself) // Lin Sanxin is 22 years old and has a deposit of 20 yuan
console.log(ohtherMyself) // Lin Sanxin, 22, has 20 yuan
money = 300

effect1() // Do it again
effect2() // Do it again

console.log(myself) // Lin Sanxin is 22 years old and has a deposit of 300 yuan
console.log(ohtherMyself) // Lin Sanxin, 22, has 300 yuan

If you add an ohtherMyself, you have to write another effect, and then execute it every time. If you increase the number, don't you have to write a lot of effect function execution code every time?

track and trigger

To solve the above problem, we can use the track function to collect all the effect functions that depend on the money variable and put them in dep. why use Set in dep? Because Set can automatically de duplicate. After collection, as soon as the money variable changes, the trigger function will be executed to notify all effect functions in dep that depend on the money variable to update the dependent variable. Let's take a look at the code first, and then I'll show you through a picture. I'm afraid you'll be dizzy, ha ha.

let name = 'Lin Sanxin', age = 22, money = 20
let myself = '', ohtherMyself = ''
const effect1 = () => myself = `${name}this year ${age}Years old, deposit ${money}element`
const effect2 = () => ohtherMyself = `${age}Year old ${name}Actually ${money}element`

const dep = new Set()
function track () {
    dep.add(effect1)
    dep.add(effect2)
}
function trigger() {
    dep.forEach(effect => effect())
}
track() //Collection dependency
effect1() // Execute once first
effect2() // Execute once first
console.log(myself) // Lin Sanxin is 22 years old and has a deposit of 20 yuan
console.log(ohtherMyself) // Lin Sanxin, 22, has 20 yuan
money = 300

trigger() // Notify the variables myself and otherMyself to update

console.log(myself) // Lin Sanxin is 22 years old and has a deposit of 300 yuan
console.log(ohtherMyself) // Lin Sanxin, 22, has 300 yuan

What about the object?

The above is about basic data types. Let's talk about objects. Let me give an example to implement their response in the most primitive way

const person = { name: 'Lin Sanxin', age: 22 }
let nameStr1 = ''
let nameStr2 = ''
let ageStr1 = ''
let ageStr2 = ''

const effectNameStr1 = () => { nameStr1 = `${person.name}He's a big rookie` }
const effectNameStr2 = () => { nameStr2 = `${person.name}He's a little genius` }
const effectAgeStr1 = () => { ageStr1 = `${person.age}I'm already very old` }
const effectAgeStr2 = () => { ageStr2 = `${person.age}I'm still young` }

effectNameStr1()
effectNameStr2()
effectAgeStr1()
effectAgeStr2()
console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// Lin Sanxin is a rookie. Lin Sanxin is a little genius. At 22, he is very old. At 22, he is still very young

person.name = 'sunshine_lin'
person.age = 18

effectNameStr1()
effectNameStr2()
effectAgeStr1()
effectAgeStr2()

console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// sunshine_lin is a big rookie, sunshine_lin is a little genius. He is very old at 18. He is still very young at 18

We can also see the above code. I feel that it is very brainless.. Remember the dep collection effect mentioned earlier? Let's treat the name and age in the person object as two variables, each of which has its own dependent variables

  • name: nameStr1 and nameStr2
  • age: ageStr1 and ageStr2
    Therefore, name and age should have their own dep and collect the effect s corresponding to their dependent variables

As mentioned earlier, dep uses Set. Because person has age and name attributes, it has two DEPs. What is used to store these two DEPs? We can use another data structure Map of ES6 to store

const person = { name: 'Lin Sanxin', age: 22 }
let nameStr1 = ''
let nameStr2 = ''
let ageStr1 = ''
let ageStr2 = ''

const effectNameStr1 = () => { nameStr1 = `${person.name}He's a big rookie` }
const effectNameStr2 = () => { nameStr2 = `${person.name}He's a little genius` }
const effectAgeStr1 = () => { ageStr1 = `${person.age}I'm already very old` }
const effectAgeStr2 = () => { ageStr2 = `${person.age}I'm still young` }

const depsMap = new Map()
function track(key) {
    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, dep = new Set())
    }
    // Let's write it down for now
    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') // Collect person Name dependency
track('age') // Collect person Age dependency



effectNameStr1()
effectNameStr2()
effectAgeStr1()
effectAgeStr2()
console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// Lin Sanxin is a rookie. Lin Sanxin is a little genius. At 22, he is very old. At 22, he is still very young

person.name = 'sunshine_lin'
person.age = 18

trigger('name') // Notify person Name dependent variable update
trigger('age') // Notify person Dependent variable update for age

console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// sunshine_lin is a big rookie, sunshine_lin is a little genius. He is very old at 18. He is still very young at 18

We have only one person object above. What if there are multiple objects? What should I do? As we all know, each object will create a Map to store the dep of the attributes in the object (using Set). If there are multiple objects, what should be used to store the Map corresponding to each object? Please see the figure below

In fact, ES6 also has a new data structure called WeakMap. Let's use it to store the maps of these objects. So we have to transform the track function and trigger function to see what they look like before

const depsMap = new Map()
function track(key) {
    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, dep = new Set())
    }
    // Let's write it down for now
    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())
    }
}

In the previous code, only a single object was processed, but now if you want multiple objects, you have to use WeakMap for transformation (the next code may be a little wordy, but it will take care of students with weak foundation)

const person = { name: 'Lin Sanxin', 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())
    }
    // Let's write it down for now
    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())
        }
    }
}

After the above transformation, we finally realized multi-object dependency collection. Let's have a try

const person = { name: 'Lin Sanxin', 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}He's a big rookie` }
const effectNameStr2 = () => { nameStr2 = `${person.name}He's a little genius` }
const effectAgeStr1 = () => { ageStr1 = `${person.age}I'm already very old` }
const effectAgeStr2 = () => { ageStr2 = `${person.age}I'm still young` }
const effectTypeStr1 = () => { typeStr1 = `${animal.type}He's a big rookie` }
const effectTypeStr2 = () => { typeStr2 = `${animal.type}He's a little genius` }
const effectHeightStr1 = () => { heightStr1 = `${animal.height}It's already very high` }
const effectHeightStr2 = () => { heightStr2 = `${animal.height}Pretty short` }

track(person, 'name') // Collect person Name dependency
track(person, 'age') // Collect person Age dependency
track(animal, 'type') // animal.type dependency
track(animal, 'height') // Collect animal Dependence of height



effectNameStr1()
effectNameStr2()
effectAgeStr1()
effectAgeStr2()
effectTypeStr1()
effectTypeStr2()
effectHeightStr1()
effectHeightStr2()

console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// Lin Sanxin is a rookie. Lin Sanxin is a little genius. At 22, he is very old. At 22, he is still very young

console.log(typeStr1, typeStr2, heightStr1, heightStr2)
// dog is a big rookie. dog is a little genius. 50 is already very tall. 50 is still very short

person.name = 'sunshine_lin'
person.age = 18
animal.type = 'cat'
animal.height = 20

trigger(person, 'name')
trigger(person, 'age')
trigger(animal, 'type')
trigger(animal, 'height')

console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// sunshine_lin is a big rookie, sunshine_lin is a little genius. He is very old at 18. He is still very young at 18

console.log(typeStr1, typeStr2, heightStr1, heightStr2)
// The cat is a rookie. The cat is a little genius. 20 is already very tall. 20 is still very short

Proxy

Through the above learning, we can realize that when the data is updated, its dependent variables also change, but there are still disadvantages. You can find that every time we have to manually execute the track function to collect dependencies, and when the data changes, we have to manually execute the trigger function to notify the update

So, is there any way to automatically collect dependencies and automatically notify updates? The answer is yes. Proxy can solve this problem for us. Let's write a reactive function first. Let's knock it first and understand the relationship between proxy track trigger. Later, I will talk about why proxy needs to be combined with Reflect

function reactive(target) {
    const handler = {
        get(target, key, receiver) {
            track(receiver, key) // Collect dependencies on access
            return Reflect.get(target, key, receiver)
        },
        set(target, key, value, receiver) {
            Reflect.set(target, key, value, receiver)
            trigger(receiver, key) // Automatically notify updates when set
        }
    }

    return new Proxy(target, handler)
}

Then change the previous code, remove the manual track and manual trigger, and find that the previous effect can also be achieved

const person = reactive({ name: 'Lin Sanxin', age: 22 }) // Incoming reactive
const animal = reactive({ type: 'dog', height: 50 }) // Incoming reactive

effectNameStr1()
effectNameStr2()
effectAgeStr1()
effectAgeStr2()
effectTypeStr1()
effectTypeStr2()
effectHeightStr1()
effectHeightStr2()

console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// Lin Sanxin is a rookie. Lin Sanxin is a little genius. At 22, he is very old. At 22, he is still very young

console.log(typeStr1, typeStr2, heightStr1, heightStr2)
// dog is a big rookie. dog is a little genius. 50 is already very tall. 50 is still very short

person.name = 'sunshine_lin'
person.age = 18
animal.type = 'cat'
animal.height = 20

console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// sunshine_lin is a big rookie, sunshine_lin is a little genius. He is very old at 18. He is still very young at 18

console.log(typeStr1, typeStr2, heightStr1, heightStr2)
// The cat is a rookie. The cat is a little genius. 20 is already very tall. 20 is still very short

Some students may be a little confused about the above code, or they may be a little confused. I thought it would be compressed by explaining the flow chart to you through a chart. I suggest you click to have a look

Solve the problem of dead writing

There's a place above where we're dead. Do you remember, it's in the track function

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())
    }
    // Let's write it down for now
    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)
        }
    }
}

In actual development, there must be more than two objects. If you add one more object, you have to add an else if judgment. That's absolutely impossible. So how do we solve this problem? In fact, it's not difficult. The authors of Vue3 have come up with a very clever way to use a global variable activeEffect to skillfully solve this problem. How to solve it? In fact, it is very simple. As soon as each effect function is executed, it will put itself into the corresponding dep, so there is no need to write it dead.

How can we achieve this function? We need to modify the effect function and the track function

let activeEffect = null
function effect(fn) {
    activeEffect = fn
    activeEffect()
    activeEffect = null // It becomes null immediately after execution
}
function track(target, key) {
    // If activeEffect is null at this time, the following steps will not be executed
    // The judgment here is to avoid, for example, console Log (person. Name) and trigger 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) // Add the active effect at this time
}

// Each effect function is executed like this
effect(effectNameStr1)
effect(effectNameStr2)
effect(effectAgeStr1)
effect(effectAgeStr2)
effect(effectTypeStr1)
effect(effectTypeStr2)
effect(effectHeightStr1)
effect(effectHeightStr2)

Implement ref

This is how we use ref in Vue3

let num = ref(5)

console.log(num.value) // 5

Then num will become a responsive data, and you need to write num.value in this way to use num

Implementing ref is actually very simple. We have implemented reactive above. We only need to do so to implement Ref

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

We can try how it works

let num = ref(5)

effect(() => sum = num.value * 100)

console.log(sum) // 500

num.value = 10

console.log(sum) // 1000

Implement computed

Let's simply implement computed by the way. It's actually very simple

function computed(fn) {
    const result = ref()
    effect(() => result.value = fn()) // Execute computed incoming function
    return result
}

Let's see the results

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) // 40
console.log(sum2.value) // 400

num1.value = 10

console.log(sum1.value) // 80
console.log(sum2.value) // 800

num2.value = 16

console.log(sum1.value) // 160
console.log(sum2.value) // 1600

Since then, we have realized all the functions of this article

Final code

const targetMap = new WeakMap()
function track(target, key) {
    // If activeEffect is null at this time, the following steps will not be executed
    // The judgment here is to avoid, for example, console Log (person. Name) and trigger 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) // Add the active effect at this time
}
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) // Collect dependencies on access
            return Reflect.get(target, key, receiver)
        },
        set(target, key, value, receiver) {
            Reflect.set(target, key, value, receiver)
            trigger(receiver, key) // Automatically notify updates when set
        }
    }

    return new Proxy(target, handler)
}
let activeEffect = null
function 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 and Reflect

Proxy

const person = { name: 'Lin Sanxin', age: 22 }

const proxyPerson = new Proxy(person, {
    get(target, key, receiver) {
        console.log(target) // Original person
        console.log(key) // Attribute name
        console.log(receiver) // proxyPerson after proxy
    },
    set(target, key, value, receiver) {
        console.log(target) // Original person
        console.log(key) // Attribute name
        console.log(value) // Set value
        console.log(receiver) // proxyPerson after proxy
    }
})

proxyPerson.name // Access property trigger get method

proxyPerson.name = 'sunshine_lin' // Setting the property value triggers the set method

Reflect

Here are two methods of Reflect

  • get(target, key, receiver): my personal understanding is to access the key attribute of target, but this points to the receiver, so the actual accessed value is the value of the receiver's key, but this is not a direct access to the receiver[key] attribute. We should distinguish it
  • set(target, key, value, receiver): my personal understanding is to set the key attribute of target to value, but this points to the receiver, so in fact, the value of the receiver's key is set to value, but this is not a direct receiver[key] = value. We should distinguish it

As we emphasized above, you can't directly receive [key] or receive [key] = value, but through reflect Get and reflect Set, go around the corner to access properties or set properties. Why? Let's take a counter example

const person = { name: 'Lin Sanxin', age: 22 }

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

console.log(proxyPerson.name)

proxyPerson.name = 'sunshine_lin' 
// An error will be reported directly, and the stack memory overflows. Maximum call stack size exceeded

Why? Look at the solution below

Now you know why you can't directly receive [key] or receive [key] = value, because such a direct operation will lead to an infinite loop and eventually an error. So the right thing to do is

const person = { name: 'Lin Sanxin', 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) // Lin Sanxin

proxyPerson.name = 'sunshine_lin'

console.log(proxyPerson.name) // sunshine_lin

Some students must ask. It's OK to write so below. Why not suggest it? I'll put it down and say it together

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

Why use it together

In fact, Proxy can be used without reflection. We can write this, and we can still achieve the desired effect

const person = { name: 'Lin Sanxin', 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) // Lin Sanxin

proxyPerson.name = 'sunshine_lin'

console.log(proxyPerson.name) // sunshine_lin

Then why do you recommend using Proxy and Reflect together? Because Proxy and Reflect methods are one-to-one correspondence, using Reflect in Proxy will improve semantics

  • Get of Proxy corresponds to reflect get
  • Set of Proxy corresponds to reflect set
  • There are many other methods I will not list one by one, but they all correspond one by one

Another reason is to try to put this on the receiver instead of the target

Why try to put this on the proxy object receiver instead of the original object target? Because the original object target may also be the proxy object of another agent, if this is always placed on the target, the probability of bug s will be greatly improved. Therefore, why not recommend it in the previous code? You should know?

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

epilogue

I'm Lin Sanxin, an enthusiastic front-end rookie programmer. If you are self-motivated, like the front end and want to learn from the front end, we can make friends and fish together. Ha ha, fish school, add me, please note [Si no]

Keywords: Javascript Front-end Vue.js Interview source code

Added by webzyne on Wed, 05 Jan 2022 05:48:10 +0200