This is the third day of my participation in the August More text Challenge. For details, see:August is more challenging

Vue Watch is a great feature point to learn from. It is very beautiful to use when you write plug-ins or small demo, which can improve your coding speed

watch

  • Let’s implement a watch of our own
  • Implement data monitoring

Implementations of listening on objects and strings

  • Set the value of the data binding and the corresponding dispatcher function data/data/data/watch
  • Using Object. DefineProperty get and set to intercept and distribute data

To define functions first Observe data is our data data is our data data is our watch is a function of let’s do data to monitor trigger to use (the equivalent of vue watch the handler ())

The data definition and the corresponding dispatcher function are defined

  1. Defines a string type myString
  2. Define a myBoolean of type Boolean
  3. Define a myObject of type object
// Enter the relevant code
class Observe {
  constructor() {
    this.$data = {
      myString: 'juejin'.myBoolean: true.myObject: {name: 'juejin'}}this.$watch = {
       myString: this.myStringFn,
       myBoolean: this.myBooleanFn,
       myObject: this.myObjectFn
    }
  }
  
  myStringFn() {}
  
  myBooleanFn() {}
  
  myObjectFn(){}}Copy the code

Using Object. DefineProperty get and set to intercept and distribute data

Object. DefineProperty MDN

  1. The object.defineProperty () method directly defines a new property on an Object, or modifies an existing property on an Object, and returns the Object

  2. Object.defineproperty (obj, prop, descriptor)

  3. The description of a different property can be changed and deleted from the corresponding object only when the value of the different key is set to true

  4. Enumerable: This property appears in the enumeration property of an object if and only if its Enumerable key value is true

  5. Value: indicates the value of the attribute. Can be any valid JavaScript value (value, object, function, etc.)

  6. Writable: The value of the property can be changed only if the property’s writable key is true

  7. Get: The getter function for the property, or undefined if there is no getter. This function is called when the property is accessed.

  8. Set: the setter function for the property, or undefined if there is no setter. This function is called when the property value is modified.

  • Now that we know the properties it’s time to start our code as follows
  • increaseobserveranddefinePropertyfunction
class Observe {
  constructor() {
    this.$data = {
      myString: 'juejin'.myBoolean: true.myObject: {name: 'juejin'}}this.$watch = {
       myString: this.myStringFn,
       myBoolean: this.myBooleanFn,
       myObject: this.myObjectFn
    }
    
     / / binding
     this.observer(this.$data);
  }
  
  myStringFn(d,old) {
   console.log('I'm the string that the nugget watch listens to', d)
  }
  
  myBooleanFn(d,old) {
    console.log('I'm the type of Boolean the Nuggets watch listens to', d)
  }
  
  myObjectFn(d, old) {
    console.log('I am the type of object on which the nugget watch listens', d)
  }
  
  observer(data) {
     if (typeofdata ! = ='object' || data == null) {
      return;
    }
    // Loop object binding
    for (let key in data) {
      this.defineProperty(key); }}defineProperty(_key) {
    Object.defineProperty(this, _key, {
      get: function () {
        return this.$data[_key];
      },

      set: function (val) {
        const oldVal = cloneDeep(this.$data[_key]);
        if (oldVal === val) return val;
        this.$data[_key] = val;
        if(!!!!!this.$watch[_key] && (typeof (this.$watch[_key]) === 'function')) {
          this.$watch[_key].call(this, val, oldVal);
        }
        
        return val;
      },

      enumerable: true.configurable: true}); }}Copy the code

Verify that the object and string are heard and dispatch the corresponding function

let observer = new Observer();
/ / change the string
observer.myString = 'jun-jin-test';
// Output: I'm a jun-jin-test string monitored by the nugget watch

/ / change the Boolean
observer.myBoolean = false;
// Output: I am the Boolean type that the nugget watch listens to false

// Change the object
observer.myObject = {
    name: 'Nugget text'
};
// Output: I'm the type of object the watch listens to {name: "nugget text "}
Copy the code

Verification by

  • Output is correct

Array listener implementation

Everyone knows that object.defineProperty can’t listen to arrays, so how do you do that?

  • Vue is to override the array prototype methods and then implement the array distribution mechanism

Override the methods on the prototype of the array, and then those methods can be heard when they are called. Implement the dispatch mechanism

  • The following code
  1. Changing the Observer function
  2. Rewrite the array prototype method
  3. Add some properties to assist the array distribution mechanism
  4. new$data/$watchBind to an array
class Observe {
  constructor() {
    this.$data = {
      myString: 'juejin'.myBoolean: true.myObject: {name: 'juejin'},
      myArray: ['the nuggets'.'sanyuan']}this.$watch = {
       myString: this.myStringFn,
       myBoolean: this.myBooleanFn,
       myObject: this.myObjectFn,
       myArray: this.myArrayFn,
    }
    
     / / binding
     this.observer(this.$data);
  }
  
  myStringFn(d,old) {
   console.log('I'm the string that the nugget watch listens to', d)
  }
  
  myBooleanFn(d,old) {
    console.log('I'm the type of Boolean the Nuggets watch listens to', d)
  }
  
  myObjectFn(d, old) {
    console.log('I am the type of object on which the nugget watch listens', d)
  }
  
  myArrayFn(d,old) {
   console.log('I am the Array type on which the nugget watch listens', d)
  }
  
  observer(data) {
    if (typeofdata ! = ='object' || data == null) {
      return;
    }
    
    let _this = this;
    let originalProto = Array.prototype;
     // Create an Array prototype
    let arrayProto = Object.create(originalProto);
   
    const methodsToPatch = [
      'push'.'pop'.'shift'.'unshift'.'splice'.'sort'.'reverse'
    ];

    methodsToPatch.forEach(method= > {
      arrayProto[method] = function () {
        console.log('Array method called')
        const oldVal = _this.$data[this.notify];
        originalProto[method].apply(this.arguments);
        
        // Implement the dispatch mechanism
        _this.$watch[this.notify].call(_this, this, oldVal);
      };
    });



    for (let key in data) {
      if (Array.isArray(data[key])) {
        // Bind a notify to the array method specifying the required key
        arrayProto.notify = key;
        // Override __proto__ on the array
        data[key].__proto__ = arrayProto;
      }

      this.defineProperty(key); }}defineProperty(_key) {
    Object.defineProperty(this, _key, {
      get: function () {
        return this.$data[_key];
      },

      set: function (val) {
        const oldVal = cloneDeep(this.$data[_key]);
        if (oldVal === val) return val;
        this.$data[_key] = val;
        if(!!!!!this.$watch[_key] && (typeof (this.$watch[_key]) === 'function')) {
          this.$watch[_key].call(this, val, oldVal);
        }
        
        return val;
      },

      enumerable: true.configurable: true}); }}Copy the code

Verify that the array is listening and dispatch the pair function

// Change the array
observer.myArray.push('push进来的');
// Output: the Array method was called and I am the Array type (3) monitored by the nugget watch [" nugget ", "triplet ", "push in "]
Copy the code

Verify that the array method succeeded

  • Authentication is successful

conclusion

  • Hello, I’m Sanyuan
  • Thank you for watching. I hope it helps you
  • Have a problem can communicate together ah

Source code and test code

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    
</body>
</html>


<script>
class Observer {
    constructor() {
    this.$data = {
      myString: 'juejin'.myBoolean: true.myObject: {name: 'juejin'},
      myArray: ['the nuggets'.'sanyuan']}this.$watch = {
       myString: this.myStringFn,
       myBoolean: this.myBooleanFn,
       myObject: this.myObjectFn,
       myArray: this.myArrayFn,
    }
    
     / / binding
     this.observer(this.$data);
  }
  
  myStringFn(d,old) {
   console.log('I'm the string that the nugget watch listens to', d)
  }
  
  myBooleanFn(d,old) {
    console.log('I'm the type of Boolean the Nuggets watch listens to', d)
  }
  
  myObjectFn(d, old) {
    console.log('I am the type of object on which the nugget watch listens', d)
  }
  
  myArrayFn(d,old) {
   console.log('I am the Array type on which the nugget watch listens', d)
  }
  
  observer(data) {
    if (typeofdata ! = ='object' || data == null) {
      return;
    }
    
    let _this = this;
    let originalProto = Array.prototype;
     // Create an Array prototype
    let arrayProto = Object.create(originalProto);
   
    const methodsToPatch = [
      'push'.'pop'.'shift'.'unshift'.'splice'.'sort'.'reverse'
    ];

    methodsToPatch.forEach(method= > {
      arrayProto[method] = function () {
        console.log('Array method called')
        const oldVal = _this.$data[this.notify];
        originalProto[method].apply(this.arguments);
        
        // Implement the dispatch mechanism
        _this.$watch[this.notify].call(_this, this, oldVal);
      };
    });



    for (let key in data) {
      if (Array.isArray(data[key])) {
        // Bind a notify to the array method specifying the required key
        arrayProto.notify = key;
        // Override __proto__ on the array
        data[key].__proto__ = arrayProto;
      }

      this.defineProperty(key); }}defineProperty(_key) {
    Object.defineProperty(this, _key, {
      get: function () {
        return this.$data[_key];
      },

      set: function (val) {
        const oldVal = (this.$data[_key]);
        if (oldVal === val) return val;
        this.$data[_key] = val;
        if(!!!!!this.$watch[_key] && (typeof (this.$watch[_key]) === 'function')) {
          this.$watch[_key].call(this, val, oldVal);
        }
        
        return val;
      },

      enumerable: true.configurable: true}); }}let observer = new Observer();
/ / change the string
observer.myString = 'jun-jin-test';
// Output: I'm a jun-jin-test string monitored by the nugget watch

/ / change the Boolean
observer.myBoolean = false;
// Output: I am the Boolean type that the nugget watch listens to false

// Change the object
observer.myObject = {
    name: 'Nugget text'
};
// Output: I'm the type of object the watch listens to {name: "nugget text "}

// Change the array
observer.myArray.push('push进来的');
// Output: the Array method was called and I am the Array type (3) monitored by the nugget watch [" nugget ", "triplet ", "push in "]
</script>
Copy the code