Taking the classic Getters/Setters from Java and C#, the author discusses vue.js from component rendering functions, Getters for data, Setter hijacking, listener control, and rerendering to trigger the entire flow of life. Understanding vue.js Reactivity in Depth with object.defineProperty ()

primers

Coming from a Java background, JavaScript was a bit strange when I first started many years ago because it didn’t have getters and setters. Over time, I’ve come to like this missing feature because it makes code simpler than Java’s plethora of getters and setters. For example, let’s look at the following Java code:

class Person{
  String firstName;
  String lastName;

  // this Demo omitted some constructor code :)

  public void setFirstName(firstName) {
    this.firstName = firstName;
  }

  public String getFirstName(a) {
    return firstName;
  }

  public void setLastName(lastName) {
    this.lastName = lastName;
  }

  public String getLastName(a) {
    returnlastName; }}// Create instance
Person bradPitt = new Person();
bradPitt.setFirstName("Brad");
bradPitt.setLastName("Pitt");Copy the code

JavaScript developers never do this, instead they do this:

var Person = function () {};var bradPitt = new Person();
bradPitt.firstName = 'Brad';
bradPitt.lastName = 'Pitt';Copy the code

This is much simpler. Simplicity is usually better, isn’t it?

That’s true, but sometimes I want to get properties that can be modified, but I don’t have to know what those properties are. For example, we extend a new method getFullName() in Our Java code:

class Person{
  private String firstName;
  private String lastName;

  // this Demo omitted some constructor code :)

  public void setFirstName(firstName) {
    this.firstName = firstName;
  }

  public String getFirstName(a) {
    return firstName;
  }

  public void setLastName(lastName) {
    this.lastName = lastName;
  }

  public String getLastName(a) {
    return lastName;
  }

  public String getFullName(a) {
    return firstName + "" + lastName;
  }
}

Person bradPitt = new Person();
bradPitt.setFirstName("Brad");
bradPitt.setLastName("Pitt");

// Prints 'Brad Pitt'
System.out.println(bradPitt.getFullName());Copy the code

In the example above, fullName is a calculated property that is not private but always returns the correct result.

C# and implicit getters /setters

Let’s take a look at one of C#’s features that I really like: implicit getters/setters. In C#, you can define getters/setters if you want, but you don’t have to, but if you decide to do so, the caller doesn’t have to call the function. The caller only needs to access the property directly, and the getter/setter is automatically run in the hook function:

public class Foo
{
    public string FirstName {get; set; }public string LastName {get; set; }public string FullName {get { return firstName + "" + lastName }; private set;}
}Copy the code

I think it’s pretty cool…

Now, if I wanted to implement something similar in JavaScript, I would waste a lot of time, such as:

var person0 = {
  firstName: 'Bruce'.lastName: 'Willis'.fullName: 'Bruce Willis'.setFirstName: function (firstName) {
    this.firstName = firstName;
    this.fullName = `The ${this.firstName} The ${this.lastName}`;
  },
  setLastname: function (lastName) {
    this.lastName = lastName;
    this.fullName = `The ${this.firstName} The ${this.lastName}`; }};console.log(person0.fullName);
person0.setFirstName('Peter');
console.log(person0.fullName);Copy the code

It will print:

"Bruce Willis"
"Peter Willis"Copy the code

But using setXXX(value) isn’t ‘javascripty’ enough.

The following ways can solve this problem:

var person1 = {
  firstName: 'Brad'.lastName: 'Pitt'.getFullName: function () {
    return `The ${this.firstName} The ${this.lastName}`; }};console.log(person1.getFullName()); // Print "Brad Pitt"Copy the code

Now let’s go back to the getter that was computed. You can set first or last name and simply merge their values:

person1.firstName = 'Peter'
person1.getFullName(); // return "Peter Pitt"Copy the code

This is more convenient, but I still don’t like it because we have to define a method called “getxxx()”, which isn’t ‘javascripty’ enough either. I’ve been thinking about how to use JavaScript better for years.

Then Vue came along

On my Youtube channel, many videos related to Vue tutorials show that I’m used to reactive development, which in the earlier Angular1 era we called Data Binding. It looks simple. You just define some data in the Vue instance’s data() block and bind it to HTML:

var vm = new Vue({
  data() {
    return {
      greeting: 'Hello world! '}; }})Copy the code
<div>{greeting}</div>Copy the code

Apparently it prints “Hello World!” in the user interface. .

Now, if you change the value of “greeting”, the Vue engine will react and update the view accordingly.

methods: {
  onSomethingClicked() {
    this.greeting = "What's up"; }},Copy the code

For a long time I wondered, how does it work? An event is triggered when a property of an object changes, right? Or does Vue keep calling setInterval to check for updates?

By reading the official Vue documentation, I learned that changing an object property implicitly calls the getter/setter, notifying the observer again, and triggering a re-rendering, as shown below. This example is from the official Vue.

But I also want to know:

  • How to get data to come with getter/setters?

  • What are the internals of these implicit calls?

The first problem is simple: Vue has everything ready for us. When you add new data, Vue will add getters /setters to it through its properties. But I set foo.bar = 3? What happens?

The answer to this question appeared in a Twitter conversation I had with SVG & Vue expert Sarah Drasner:

Timo: foo.bar=value; How does it respond in real time? Timo: But this article doesn’t explain the issues mentioned above. Timo: They are like: assign a value -> call the setter-> notify the observer, not understanding why the setter/getter exists without using setInterval and Event. Sarah: All the data you fetch is propped in the Vue instance data{}.

Obviously, it was also the official documentation for reference, and I had read it before, so I started reading the Vue source code to better understand what was going on. After a while I remembered seeing a method called Object.defineProperty() in the official documentation. I found it as follows:

/** * Defines the attributes of the response to the object */
export function defineReactive (obj: Object, key: string, val: any, customSetter? :? Function, shallow? : boolean) {
  const dep = new Dep()

  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  // Pre-define getter/setters
  const getter = property && property.get
  const setter = property && property.set

  letchildOb = ! shallow && observe(val)Object.defineProperty(obj, key, {
    enumerable: true.configurable: true.get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
        }
        if (Array.isArray(value)) {
          dependArray(value)
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* Disables esLint from making self-comparisons */
      if(newVal === value || (newVal ! == newVal && value ! == value)) {return
      }
      /* Enable ESLint to not compare itself */
      if(process.env.NODE_ENV ! = ='production' && customSetter) {
        customSetter()
      }
      if (setter) {
        setter.call(obj, newVal)
      } else{ val = newVal } childOb = ! shallow && observe(newVal) dep.notify() } }) }Copy the code

So the answer is always there in the documentation:

Pass a normal JavaScript Object to the Vue instance’s data option, and Vue iterates through all of the Object’s properties and converts them into getters/setters using Object.defineProperty. Object.defineproperty is a feature that is only supported by ES5 and cannot be shim, which is why Vue does not support IE8 and earlier browsers.

I just want to briefly understand what Object.defineProperty() does, so I’ll give you a quick example:

var person2 = {
  firstName: 'George'.lastName: 'Clooney'};Object.defineProperty(person2, 'fullName', {
  get: function () {
    return `The ${this.firstName} The ${this.lastName}`; }});console.log(person2.fullName); // Print "George Clooney"Copy the code

Remember the implicit getters in C# at the beginning of this article? They look similar, but ES5 just started supporting them. All you need to do is define an existing Object using Object.defineProperty() and when to get that property. This getter is called reactive — this is actually what Vue does behind the scenes when you add new data.

Does object.defineProperty () make Vue simpler?

After all this, I’ve been wondering if Object.defineProperty() can make Vue simpler. Is it really necessary to make things too complicated for beginners to understand (as is Redux) with the increasing number of new terms:

  • Mutator – maybe you’re talking about (implicit) setters

  • Getters – Why not replace Object.defineProperty() with (implicit) getter

  • Store.mit () – why not simplify to foo = bar, but store.mit (“setFoo”, bar); ?

What do you think? Does Vuex have to be complex or can it be as simple as Object.defineProperty()?

Copyright Notice: All articles on this Blog are licensed under a CC BY-NC-SA 3.0CN license unless otherwise specified. Reprint please indicate the source!