Originally published in: Holmeshe. me, this article is a Chinese remake.

This series is serialized simultaneously on Medium.

I remember doing a lot of crap with Ajax early on and being impressed by JS as “very weird”. Recently CHANGED a job, the working language is JS. And then I found that this language is really amazing, both front and back, basically become a synonym for full stack. So this is a good opportunity to learn the language systematically. Because it’s for programmers, this series won’t cover basic “if-else”, looping, or object orientation. Instead, I’ll focus on the differences and hopefully give you a code-walking learning experience in the Pull Request!

Class inheritance can be implemented in JavaScript using prototype chains. Any object can use its own bound prototype, like a paternity test in the prototype chain back to their own father, and grandfather and so on. The prototype chain also naturally supports the ultimate purpose of inheritance, code reuse: when accessing a member that does not exist in the current object, the JS engine will start with the current object and look up the member information in the integration tree up the prototype chain.

Simple but not easy

I’m going to implement the prototype chain step by step, so this reading is more like a simulation of real development, which I think will be easier to remember. In code:

var LaoWang = function() {
  this.aproperty = 'a';
  this.amethod = function () {
    return 'b';
  };
};


var XiaoMing = function() {};

XiaoMing.prototype = LaoWang.prototype; //let's make the chain var subobj = new XiaoMing(); alert(subobj instanceof LaoWang); alert(subobj.aproperty); alert(subobj.amethod());Copy the code

Running results:

true
undefined

Uncaught TypeError: subobj.amethod is not a functionCopy the code

Although the type is correctly identified, there are problems with member access. So as a rule of thumb, MY guess is that the parent class’s constructor is untuned. Try it on:

  var LaoWang = function() {
    this.aproperty = 'a';
    this.amethod = function () {return 'b'; }; }; var XiaoMing =function() {
++  LaoWang();
  };

  XiaoMing.prototype = LaoWang.prototype;
++XiaoMing.prototype.constructor = XiaoMing;
  var subobj = new XiaoMing();

  alert(subobj instanceof LaoWang);
  alert(subobj.aproperty);
  alert(subobj.amethod());Copy the code


Still no. Yes, yes, yes, yes, yes, yes, yes, yes, yes, yes, yes, yes. If you haven’t guessed, please ⬅ to my previous article.


As mentioned in the article above, to fix this problem, we can:

var tmpf = LaoWang.bind(this);
tmpf();Copy the code

Can also:

LaoWang.apply(this);Copy the code

I use the second one because it saves one line of code:

var LaoWang = function() {
    this.aproperty = 'a';
    this.amethod = function () {return 'b'; }; }; var XiaoMing =function() {
++  LaoWang.apply(this);
  };

  XiaoMing.prototype = LaoWang.prototype;
  XiaoMing.prototype.constructor = XiaoMing;
  var subobj = new XiaoMing();

  alert(subobj instanceof LaoWang);
  alert(subobj.aproperty);
  alert(subobj.amethod());Copy the code

Running results:

  true
  a
  bCopy the code

Done! ?

  var LaoWang = function() {
    this.aproperty = 'a';
    this.amethod = function () {return 'b'; }; }; var XiaoMing =function() {
    LaoWang.apply(this);
  };

  XiaoMing.prototype = LaoWang.prototype;
  XiaoMing.prototype.constructor = XiaoMing;

++XiaoMing.prototype.another_property = 'c';
  var subobj = new XiaoMing();
++var superobj = new LaoWang();

++alert(subobj.another_property);
++alert(superobj.another_property);Copy the code

Running results:

c
cCopy the code

In fact what we just figured out was not inheritance at all, but something else that doesn’t exist. I’ll just call him a pair.

Totally wrong direction… Much of the effort is in the right direction, albeit in the wrong direction. As I sometimes hear stories, although the protagonist failed in his business, he found one day that the experience and resources accumulated from the seemingly failed experience were directly related to his subsequent success. So let’s move on.

The prototype of decoupling

The normal way to do this is to decouple two entities, an intermediate class (which is actually an object, first class) and an intermediate object (ordinary), and we just wrote out what we don’t know, let me draw the relationship:

Subclass.prototype
          |---intermediate object
               |---.__proto__
                     |---IntermediateClass.prototype === Superclass.prototypeCopy the code

In this case, the intermediate object is an instance of the intermediate class.

Let’s go back to the code from the beginning of the previous article:

function inherits(ChildClass, ParentClass) {
  function IntermediateClass() {}
  IntermediateClass.prototype = ParentClass.prototype;
  ChildClass.prototype = new IntermediateClass;
  ChildClass.prototype.constructor = ChildClass;
  return ChildClass;
};Copy the code

After looking at the specific problem and schematic diagram, is it understood? Let’s verify:

  var LaoWang = function() {
    this.aproperty = 'a';
    this.amethod = function () {return 'b'; }; }; var XiaoMing =function() {
    LaoWang.apply(this);
  };

--XiaoMing.prototype = LaoWang.prototype;
++inherits(XiaoMing, LaoWang);
  XiaoMing.prototype.another_property = 'c';
  var subobj = new XiaoMing();
  var superobj = new LaoWang();
  
  alert(subobj instanceof LaoWang);
  alert(subobj instanceof XiaoMing);
  alert(subobj.aproperty);
  alert(subobj.amethod());
  alert(subobj.another_property);
  alert(superobj instanceof LaoWang);
  alert(superobj instanceof XiaoMing);
  alert(superobj.aproperty);
  alert(superobj.amethod());
  alert(superobj.another_property);Copy the code

In fact, a better version of inherits() can be implemented with object.create () :

function inherits(ChildClass, ParentClass) {
--  function IntermediateClass() {}
--  IntermediateClass.prototype = ParentClass.prototype;
--  ChildClass.prototype = new IntermediateClass;
++  ChildClass.prototype = Object.create(ParentClass.prototype);
    ChildClass.prototype.constructor = ChildClass;
    return ChildClass;
};Copy the code

Writing so much, the verification of the time there is a little nervous 🙂

true
true
a
b
c
// this line is artificial
true
false
a
b
undefinedCopy the code

All right, you can leave on time!

I have one last point to leave unsaid:

prototype.constructor

As you may have noticed, I slipped in a little code here but didn’t say anything about it. I’m doing that on purpose, because it makes the logic a little bit smoother. Let’s take a look at this… Well, it’s a long story. Let me draw another picture.

class <----------------| |------.prototype.constructor |------...... (I have drawn this part)Copy the code

Prototype. constructor is actually a special method (again, first class object) that points to the class itself. Normally this is done by the JS engine at runtime:

var LaoWang = function() {
  this.aproperty = 'a';
  this.amethod = function () {return 'b'; }; }; alert(LaoWang.prototype.constructor === LaoWang);Copy the code

Running results:

true

But I’m in this function

function inherits(ChildClass, ParentClass)
Copy the code

When we were building the prototype chain inside, we put

ChildClass.prototype.constructor

The original value of the pointer is messed up, so we need to set it back:

ChildClass.prototype.constructor = ChildClass;Copy the code

All right, that’s it for today. If you think this article is good, you can pay attention to my public number – full stack hunter.



I can also go to Medium and slap my other articles. Thanks for reading! 👋