Suggest English can also friends directly read the original:
Skyronic.com/blog/vuejs-…The original text is not difficult. If you find it difficult to understand, you can read my article, I will add my own remarks in the obscure parts of the article (
It is represented as an inline block of code) Easy to understand. This is my first time to translate articles, if there are wrong places also hope to correct, thank you!

Body:

In this article, we’ll see how Computed works by implementing a simple version of a function that has the same functionality as computed in Vue.

JS properties:

JavaScript has a feature called object.defineProperty that can do a lot of things, but I’ll focus on just one of these methods in this article:

var person = {};

Object.defineProperty (person, 'age', {
  get: function () {
    console.log ("Getting the age");
    return 25; }});console.log ("The age is ", person.age);

// Prints:
//
// Getting the age
// The age is 25
Copy the code

Obeject.defineproperty is a method of Object. The first argument is the name of the Object, the second argument is the name of the property to be set, and the third argument is an Object that sets whether the property is modifiable, writable, and so on. This article uses obeject.defineProperty's accessor properties. Interested friends can Google or view Js high and programming)

Although person.age looks like it accesses a property of the object, internally we’re running a function.

A basically responsive vue.js

Vue.js internally builds an object that converts normal objects into observable values (response properties). Here is a simplified example of how to add response properties:

function defineReactive (obj, key, val) {
  Object.defineProperty (obj, key, {
    get: function () {
      return val;
    },
    set: function (newValue) { val = newValue; }})};// Create an object
var person = {};

// Add responsible properties "age" and "country"
defineReactive (person, 'age'.25);
defineReactive (person, 'country'.'Brazil');

// Now you are free to use person.age
if (person.age < 18) {
  return 'minor';
}
else {
  return 'adult';
}

// Set the value of person.country
person.country = 'Russia';Copy the code

Interestingly, 25 and ‘Brazil’ are also variables inside the closure, and val changes only when new values are assigned to them. Person. country does not have the value ‘Brazil’, but the getter function has the value ‘Brazil’.

Declare a computed property

Let’s create a function defineComputed that defines the evaluated properties. This is the same function that you would normally use for computed.

defineComputed (
  person, // Compute properties are declared on this object
  'status'.// Calculate the name of the attribute
  function () { // The function that actually returns the value of the property
    console.log ("status getter called")
    if (person.age < 18) {
      return 'minor';
    }
    else {
      return 'adult'; }},function (newValue) {
    // The function that is called when the evaluated property value is updated
    console.log ("status has changed to", newValue)
  }
});

// We can use computed attributes just like normal attributes
console.log ("The person's status is: ", person.status);Copy the code

Let’s write a simple defineComputed function that supports calling a calculation method, but does not currently need it to support updateCallback.

function defineComputed (obj, key, computeFunc, updateCallback) {
  Object.defineProperty (obj, key, {
    get: function () {
      // Execute the computed function and return the value
      return computeFunc ();
    },
    set: function () {
      // Do nothing, do not need to set the value of the computed property}})}Copy the code

There are two problems with this function:

  1. The evaluation function is executed each time the evaluated property is accessedcomputeFunc ()
  2. It doesn’t know when to update(That is, when we update a property in a data, the data property is also updated in the computed property)

// I want the final function to look like this: every time person.age updates its value, person.status updates person.age = 17; // console: status = minor person.age = 22; // console: the value of status is adultCopy the code

Add a dependency

Let’s add a global variable Dep:

var Dep = {
  target: null
};
Copy the code

This is a dependency, and then we update the defineComputed function with a call operation:

function defineComputed (obj, key, computeFunc, updateCallback) {
  var onDependencyUpdated = function () {
    // TODO
  }
  Object.defineProperty (obj, key, {
    get: function () {
      // Pass the onDependencyUpdated function to dep. target
      Dep.target = onDependencyUpdated;
      var value = computeFunc ();
      Dep.target = null;
    },
    set: function () {
      // Do nothing, do not need to set the value of the computed property}})}Copy the code

Now let’s go back to the response properties we set earlier:

function defineReactive (obj, key, val) {
  // All computed properties depend on this array
  var deps = [];

  Object.defineProperty (obj, key, {
    get: function () {
      // Check whether the evaluated attribute is called. If so, depart. target will be equal to an onDependencyUpdated function
      if (Dep.target) {
        // Push onDependencyUpdated function into deOS
        deps.push (target);
      }

      return val;
    },
    set: function (newValue) {
      val = newValue;

      // Notify all evaluated properties that a response property has been updated
      deps.forEach ((changeFunction) = >{ changeFunction (); }); }})};Copy the code

We can update the onDependencyUpdated function after the function that evaluates the property definition triggers an update callback.

var onDependencyUpdated = function () {
  // Recalculate the value of the computed attribute
  var value = computeFunc ();
  updateCallback (value);
}
Copy the code

Put it all together:

Let’s revisit our computed property person.status:

person.age = 22;

defineComputed (
  person,
  'status'.function () {
    if (person.age > 18) {
      return 'adult'; }},function (newValue) {
    console.log ("status has changed to", newValue)
  }
});

console.log ("Status is ", person.status);
Copy the code