Self first replaces class with prototypes. This way an object can inherit directly from another object without the need for class implementation. This is somewhat of a simplification of the Class model.

Here we have to ridicule, but we are also in the study of JS class in different ways, and even force ES6 out of the implementation of such a syntax sugar class, is really an interesting thing. In the author’s view, at least, it’s gilding the lily.

Just a quick introductionprototypeThe principle of

  • objects => containers of properties
  • prototypes => objects
  • methods => functions stored in objects

When we try to retrieve a property from object, we return it if we have it, and if we don’t, we try to retrieve it from the prototype of the current object. If prototype is not present, since prototype is itself an object, it will continue online until a property is fetched or prototype is not present.

A very simple recursion.

Why use Prototype?

So why put part of the property in Prototype instead of Object itself? It’s very simple, to share some data. Assuming that all persons have the “walk” and “speak” method, we don’t have to define all persons. We can put the shareable parts in an Object, and the other objects we want to use can be accessed directly through Prototype.

const person = {
  walk: function() {},
  speak: function() {
    console.log(`My name is ${this.name}`);
  }
}

const zhangsan = Object.create(person);
zhangsan.name = "zhangsan";

const jack = Object.create(person);
zhangsan.name = "jack";
Copy the code

In this way, you can reuse logic in object without using class at all. In fact, prototype’s most common use is to store state-independent methods.

Let’s talk about this

Let’s continue with the example above of how zhangsan.speak knows what object it is operating on when we call zhangsan.speak. Because speak may be defined in the Prototype chain, it may be defined in completely different objects. How can speak read the state of our perceived caller Zhangsan?

// zhangsan
{
  name: 'zhangsan'
}

======================================

// person
{
  walk,
  speak,
}
Copy the code

The answer, of course, is this. In this way, we don’t care if ‘speak’ is on Object or object prototype, or even in Prototype. We only care if “speak” exists or does not exist when zhangsan.speak is called. It doesn’t matter which layer it exists on, or even where it’s defined.

This only exists in method

As we talked about earlier, when function is an object property, we call it method. That is, method has callers, but function does not. That is, this always refers to the caller of method.

const speak = zhangsan.speak;
speak()
Copy the code

As mentioned above, when we turn speak into a normal function, this loses its meaning. (Non-strict mode, this points to window | global, and strict mode points to undefined). Of course, we can also specify the caller for function manually.

speak.bind(zhangsan)();
// or 
speak.call(zhangsan)
// or 
speak.apply(zhangsan)
Copy the code

The magic of this is that it has to do with how method is called. Unlike other variables, when a variable is defined, its value is already determined. This is also, for beginners, the hard part to understand, the part that often goes wrong. Here’s a very common example:

function Person() { this.speak = function (word) { console.log(word); } this.sayHello = function () { ['h', 'e', 'l', 'l', 'o'].forEach(function (w) { this.speak(w); }); }}Copy the code

The reason for this is that the forEach callback is called by array, so this is inconsistent with the external this. This has a different meaning from the same variable in the same function or block that we recognize.

What happens when we use new

For a normal function, it essentially has two roles:

  • Function is called directly
  • When function is called, use new as the constructor

In a way, new is like a syntactic sugar. Let’s see what new does:

  • Create an empty Object based on the constructor’s prototype and assign it to this, var this = object.create (prototype);
  • Bind this to function and run function
  • If function itself has no return value, use this as the default return value

In this way, we can implement a new function manually

function newFun(constructorFun, arg) {
  var tempObj = Object.create(constructorFun.prototype);
  return (constructorFun.apply(tempObj, arg) || tempObj);
}
Copy the code

Of course, since every function can be used as a constructor, we have an unwritten convention that only constructors begin with a capital letter, and all others begin with a lowercase letter.

this free

I have to say that a large part of the problem in JS comes from this. In JS, because we rely too much on the understanding of class and this in the class based language, we often have various problems. At least the author has reservations about this class. JS is turing-complete without this.

My own work has started to try not to write class/constructors as much as possible, not to write this, which is a different experience. React is ditching class based Component in favor of function based Component. Just as React differs from Angular, I think one of the attractions of JS itself is that you can either completely switch to class based or the other way around.

The above.