Objective: To understand how Object.defineProperty implements data hijacking

Reading time: 3 minutes

It works like this:

  1. Define a listener function that listens for each property of an object
  2. DefineProperty sets the GET and set methods for each attribute listened on.
  3. Listen on the object
  4. Handles objects embedded within objects
  5. Processing an array object

1. Define an object

let obj = {
  name: 'jw'
}
Copy the code

2. Define a listener

/** * if it is an object, it is iterated over and defines get and set */ for each attribute

function observer(obj) {
  if(typeof obj === 'object') {
    for (let key in obj) {
    The // defineReactive method sets get and set, as shown in step 3defineReactive(obj, key, obj[key]); }}}Copy the code

3. Define a function that handles each attribute

function defineReactive(obj, key, value) {
  Object.defineProperty(obj, key, {
    get() {
      return value;
    },
    set(val) {
      console.log('Data updated') value = val; }})}Copy the code

Ok. So far the first version has been implemented. Give it a try

observer(obj);
obj.name = 'haha'Console output:// The data is updated
Copy the code

The above implementation has been implemented to set the obj properties when the listener, and can go to execute some code. But what if objects are embedded inside objects? Such as:

let obj = {
  name: 'jw'.age: {
    old: 60}}Copy the code

Execute the following code

observer(obj);
obj.age.old = '50'Console output: nullCopy the code

4. Perform iterative processing on the monitored OBJ

// Modify defineReactive to add a line of code
function defineReactive(obj, key, value) {
  // If the property of the object is also an object. The iteration process
  observer(value);
  Object.defineProperty(obj, key, {
    //....})}Copy the code

Execute the following code:

observer(obj);
obj.age.old = '50'Console output:// The data is updated
Copy the code

Unfortunately, object.defineProperty does not work if the Object is an array, as in:

obj.skill = [1.2.3];
obj.skill.push(4); Console output:/ / empty
Copy the code

In fact, not just push, but slice, shift, unshif… It doesn’t work.

5. Rewrite the array method

let arr = ['push'.'slice'.'shift'.'unshift'];
arr.forEach(method= > {
  let oldPush = Array.prototype[method];
  Array.prototype[method] = function(value) {
    console.log('Data updated')
    oldPush.call(this, value)
  }
})
Copy the code

Execute the following code:

obj.skill = [1.2.3];
obj.skill.push(4); Console output:// The data is updated
Copy the code

However, the array length operation is still invalid. This is why arrays can only be changed by methods in VUE.

Conclusion: Object.defineProperty simply solves the problem of how to trigger notifications after a state change, and who to notify? Who cares if those properties change? More on that later.

Thanks for reading!

I am Haiming Yue, a primary school student.


The complete code below


let obj = {
  name: 'jw'.age: {
    old: '60'}}// Vue data hijacking observer.defineProperty

function observer(obj) {
  if(typeof obj === 'object') {
    for (let key inobj) { defineReactive(obj, key, obj[key]); }}}function defineReactive(obj, key, value) {
  observer(value);

  Object.defineProperty(obj, key, {
    get() {
      return value;
    },
    set(val) {
      console.log('Data updated')
      value = val;
    }
  })
}
observer(obj);


// obj.age.old = '50'


// Object.defineProperty is not valid for arrays
let arr = ['push'.'slice'.'shift'.'unshift'];

arr.forEach(method= > {
  let oldPush = Array.prototype[method];
  Array.prototype[method] = function(value) {
    console.log('Data updated')
    oldPush.call(this, value)
  }
})
obj.skill = [1.2.3];
obj.skill.push(4);
Copy the code