The vue2 data responsive core is implemented through the Object.defineProperty() API, but this API is not used much in practice. I tried to memorize it for the interview, but forgot it in a few days. Knowledge that is not understood by the brain cannot be retained after all. In this article, notes explain the use of this API and what vue2 does with it in their own reflections.

Understand the property types of objects

Before we talk about the Object.defineProperty API, how does JS know if a property in an Object can be changed?

For example, if we declare a variable object A, which has a name attribute, how do we know if the name attribute can be changed

const a = { name: 'a' }
Copy the code

We can use the Object. GetOwnPropertyDescriptor () to obtain the specified Object on a has its own corresponding attribute descriptor

const b = Object.getOwnPropertyDescriptor(a, 'name')
console.log(b) 
/ / {
// configurable: true
// enumerable: true
// value: "haha"
// writable: true
// __proto__: Object
/ /}

Copy the code

These properties are internal and cannot be seen or accessed directly because these properties are the canonical definition of the JS implementation engine and are used to describe properties’ characteristics. How to distinguish: An internal feature uses two brackets around its name, such as [[Enumerable]].

Where writable indicates whether the name property can be modified.

JS object attributes are divided into two types: data attributes and accessor attributes. Both of these attributes have four properties that describe their behavior.

  • Data attributes

    Data attributes can be defined directly, and the previously declared object A is a data attribute. Objects that we declare either through literals or through the new operator plus the Object constructor are data attributes.

    • [[64x]] : indicates whether it can be deleted and redefined through delete, whether its features can be modified, and whether it can be changed to an accessor property. The default is true

    • [[Enumerable]] : indicates whether you can return in a for-in loop. The default is true

    • [[Writable]] : indicates whether the value of an attribute can be modified. The default is true

    • [[Value]] : contains the actual Value of the attribute. The default value is undefined

  • Accessor properties

    Accessor properties cannot be defined directly, object.defineProperty () must be used

    • [[signals]] : The same as the data attribute
    • [[Enumerable]] : same as above
    • [[Get]] : Gets the function, called when the property is read. The default value is undefined.
    • [[Set]] : Set function, called when writing properties. The default value is undefined.

Object.defineProperty()

We can change the internal properties of an Object with Object.defineProperty().

Set properties to read-only

const c = {name: 'ccc'}
Object.defineProperty(c, 'name', {configurable: true.enumerable: true.writable: false,
})
c.name = 'ddd'
// test
console.log(c.name) // CCC cannot be changed
Copy the code

Set object property interception

const obj = { name: 'hh' }
const vm = {}
// Data hijacking
Object.defineProperty(vm, 'name', {
  configurable: true.enumerable: true.get: () = > {	// Read attributes
    return obj.name
  },
  set: (val) = > {	// Write attributes
    	if (val === obj.name) {
        return
      }
    	obj.name = val
  }
})
// test 
vm.name = 'a'
console.log(obj) // {name: "a"}
Copy the code

In the example above, the object VM data attribute is set to the accessor attribute, and when the VM name changes, the name of obj changes with it. With this in mind, we can simulate the reactive form in VUE.

Multi-attribute data hijacking

// Simulate vue's data option
const data = {
  name: 'hello'.count: 10
}
// Simulate the Vue instance
const vm = {}
// Change every data attribute in the object to an accessor attribute
function proxyData(data) {
  // Iterate over all properties of the data object
  Object.keys(data).forEach(key= > {
    Object.defineProperty(vm, key, {
      configurable: true.enumerable: true.// Execute when the value is fetched
      get: () = > {
        console.log('get: ', key, data[key])
        return data[key]
      },
      // Execute when set value
      set: newVal= > {
        console.log('set: ', key, newValue)
        if (newValue === data[key]) {
          return
        }
        data[key] = newValue
      }
    })
  })
}
proxyData(data)
  
// test
vm.msg = 'Hello World'
console.log(vm.msg) // Hello World
Copy the code

Conclusion: through the Object. GetOwnPropertyDescriptors () reads the properties for features, through the Object. DefineProperty characteristics into data set () feature attribute accessors and attribute.

The data properties contain different, Enumerable, Writable and Value properties

The accessor properties contain different, Enumerable, GET, and set properties

The core of responsiveness in VUE is to set the data properties of an object as accessor properties, so that operations can be performed when the object properties are obtained and set.