preface

The last time, I have learned

[THE LAST TIME] has always been a series THAT I wanted to write, aiming to build on and revisit THE front end.

But also to their own shortcomings and technology sharing.

Welcome more comments, comments and jokes.

The series of articles are published on the public account “Full Stack Front End Selection”, and the collection of my articles can be found on GitHub: Nealyang/personalBlog. The table of contents and the order of publication are provisional

First of all, I want to say that THE content of THE LAST TIME series always includes but is not limited to THE scope of THE title.

So back to prototyping, the usual question. But it seems that many skilled workers do not clear the relationship between Function and Object, prototype and __proto__. This article will introduce the systematic introduction of JavaScript inheritance from prototype to inheritance to the implementation of ES6 syntax sugar. If you can answer the following questions, you probably don’t need to spend any time reading this article

  • whytypeofjudgenullObjectType?
  • FunctionObjectWhat is the relationship?
  • newWhat exactly does the keyword do? Handwriting implementation.
  • prototype__proto__What is the relationship? When is it equal?
  • There are several ways to implement inheritance in ES5
  • How does ES6 implement a class
  • ES6 extendsWhat is the implementation principle of keywords

If you have any doubts about the above questions, then…

Review of THE LAST TIME series

  • 【THE LAST TIME】 Thoroughly understand JavaScript execution mechanics
  • 【THE LAST TIME】this: call, apply, bind

directory

Although the article is longer, it is more basic. Read the chapters as appropriate.

Note the questions at the end of the article

  • Prototype shuttle
    • Function objects and ordinary objects
    • __proto__
    • prototype
    • constructor
  • Typeof && Instanceof principle analysis
    • Typeof Basic usage
    • Typeof principle analysis
    • Instanceof basic usage
    • A brief analysis of instanceof principle
  • Inheritance implementation in ES5
    • The new keyword
      • New Handwritten version one
      • New Handwritten version 2
    • Class type inheritance
    • Constructor inheritance
    • Combinatorial inheritance
    • Primary inheritance
    • Parasitic inheritance
    • Parasitic combinatorial inheritance
  • The implementation principle of ES6 class
    • The base class
    • Add attributes
    • Add methods
    • The extend keyword
      • _inherits
      • _possibleConstructorReturn

Prototype shuttle

This is… Saying is the most basic nobody refutes, say to use somebody refute, say a lot of people to now did not comb clear nobody refutes! OK~ why so many articles, but you have not figured it out?

Before we go through the concepts, let’s put together a classic image of the gods:

  • Function Foo is a method, such as the built-in Array, String, and so on in JavaScript
  • A function Object is an Object
  • Function is function
  • These are all functions, so.__proto__Are allFunction.prototype
  • Again, String, Array, Number, Function, and Object are all functions

Old tie, if this picture is very clear, then you can skip this chapter

As always, let’s go straight to the concept.

Function objects and ordinary objects

As the saying goes, everything is an object. As we all know, there are several ways to create objects in JavaScript, such as object literals, or simply using the constructor new to create an object:

Let’s ignore what the above code makes sense for a moment. At least, we can see that they are all objects, but there are differences

Actually, in JavaScript, we divide objects into function objects and ordinary objects. Function objects are JavaScript’s function-simulated class implementations. Object and Function in JavaScript are typical Function objects.

The most intuitive thing about functional objects and ordinary objects is… Let’s go straight to the code:

function fun1(){};
const fun2 = function(){};
const fun3 = new Function('name'.'console.log(name)');

const obj1 = {};
const obj2 = new Object(a);const obj3 = new fun1();
const obj4 = new new Function(a);console.log(typeof Object);//function
console.log(typeof Function);//function
console.log(typeof fun1);//function
console.log(typeof fun2);//function
console.log(typeof fun3);//function
console.log(typeof obj1);//object
console.log(typeof obj2);//object
console.log(typeof obj3);//object
console.log(typeof obj4);//object
Copy the code

I don’t know if you see the above code there is some confusion ~ don’t worry, we sort it out bit by bit.

Obj1, obj2, obj3, obj4 are all ordinary objects, fun1, fun2, fun3 are all instances of Function (Function objects).

All Function instances are Function objects, and all others are ordinary objects, including instances of Function instances.

Everything in JavaScript is an object, and objects come from constructors.

In the figure above, what you are confused about is the relationship between Function and new Function. It actually works like this:

Function.__proto__ === Function.prototype//true
Copy the code

__proto__

First we need to make clear two points: 1️ __proto__ and constructor are unique to objects. 2️ prototype property is unique to function;

But in JavaScript, functions are also objects, so functions also have __proto__ and constructor attributes.

Take a look at the code and diagram in conjunction with the relationship between Object and Function described above

 function Person(){... };let nealyang = new Person(); 
Copy the code

Before we tease out the relationship above, let’s talk about __proto__ again.

The __proto__ example, which is more complicated, is a matter of history.

The ECMAScript specification describes Prototype as an implicit reference, but some browsers have already implemented the __proto__ attribute implicitly, making it possible to access prototype defined as an implicit attribute through the obj.__proto__ explicit attribute.

So here’s the thing: the ECMAScript specification says that Prototype should be an implicit reference:

  • throughObject.getPrototypeOf(obj)Indirectly accessing the specified objectprototypeobject
  • throughObject.setPrototypeOf(obj, anotherObj)Indirectly sets the specified objectprototypeobject
  • Some browsers are open early__proto__To allow passage throughobj.__proto__Direct access to the prototype, throughobj.__proto__ = anotherObjSet the prototype directly
  • The ECMAScript 2015 specification has to bow to the facts__proto__Attributes are incorporated as part of the specification

From the browser print, we can see that object A has a __proto__ attribute. In fact, it is just a virtual node that is deliberately rendered by developer tools so that developers can see the prototype. Although we can view it, it doesn’t actually exist on the object.

__proto__ attributes are neither iterated by for in nor found by object.keys (obj).

Object. Prototype gets /set from __proto__. Object. Prototype gets /set from __proto__.

Object.defineProperty(Object.prototype,'__proto__',{
	get(){
		console.log('get')}}); ({}).__proto__;console.log((new Object()).__proto__);
Copy the code

For a more in-depth introduction to __proto__, see Industry Leaders’ in-depth Understanding of JavaScript Prototypes.

What we need to know here is that __proto__ is unique to objects, and __proto__ is an object that points to another object, its prototype object. We can also think of it as a superclass object. It is when you are on a visit to a object attribute, if the object inside this property does not exist, then back to its __proto__ object attribute points to find (superclass object), if the superclass object is still this property does not exist, then back to its parent class __proto__ attribute points to the parent class of the parent of the search. And so on, until null is found. And this search process, also formed what we call the prototype chain.

prototype

object that provides shared properties for other objects

In the specification, prototype is defined as an object that provides shared properties to other objects. Prototype is an object in itself, but used to perform a function.

Any object can be used as a prototype of another object.

To modify the __proto__ diagram, we added prototype, which is unique to functions. ** It contains properties and methods that can be shared with all instances of a particular type. __proto__ === Person.prototype: nealYang.__proto__ === Person.prototype The prototype attribute is added to any function when it is created by default.

constructor

The constructor property is also unique to objects; it is an object that points to a function that is the object’s constructor.

Note that each object has a corresponding constructor, either by itself or inherited from it. For the constructor property alone, only the Prototype object has it. __proto__ === prototype; prototype.constructor=== the function itself; Therefore, an object created from a function can find its corresponding constructor from __proto__ even if it has no constructor attribute of its own, so any object can eventually find its corresponding constructor.

The only thing that might be special is the question I posed at the beginning. The ancestor of JavaScript archetypes: Function. It’s its own constructor. Function. Prototype === Function.__proto

For an intuitive view, we continue to add constructor to the above diagram:

Where the constructor property, the dashed line represents the inherited constructor property.

The prototype chain introduced by __proto__ looks something like this when we visually label it in the diagram

Typeof && Instanceof principle

Why does talking about archetypes and inheritance get in the way of type judgment? After all, there is a trace of contact on the principle, often the interview is also from shallow to deep, follow the twists out of the whole knowledge. So let’s talk a little bit about that.

typeof

MDN documentation click here: developer.mozilla.org/zh-CN/docs/…

Basic usage

Typeof is used to determine the typeof a variable. We can use typeof to determine number, undefined, symbol, string, function, Boolean, object. Unfortunately, typeof is a little awkward when it comes to determining object types. It does not explicitly tell you what kind of object the object belongs to.

let s = new String('abc');
typeof s === 'object'// true
typeof null;//"object"
Copy the code

The principle of the author

To understand why Typeof identifies NULL as object, we need to look at how js stores variable types. This is a bug in JavaScript design, though.

In the original implementation of JavaScript, values in JavaScript were represented by a tag representing a type and actual data values. The type label of the object is 0. Because null represents a null pointer (with a value of 0x00 on most platforms), null has a type label of 0 and typeof NULL therefore returns “object”. There was a proposed fix for ECMAScript (via opt-in), but it was rejected. This proposal causes Typeof NULL === ‘NULL’.

When storing a variable at the bottom, js stores its type information in the lowest 1-3 bits of the variable’s machine code:

  • 1: the integer
  • 110: Boolean
  • 100: string
  • 010: floating point number
  • 000: object

However, in the case of undefined and NULL, the information storage for these two values is a bit special:

  • null: All machine codes are 0
  • undefined: Is an integer of −2^30

When using typeof to determine variable types, we need to note that it is best to use Typeof to determine basic data types (including symbol) and avoid null.

Typeof is just an add-on discussion area to the instanceof we’re talking about prototypes

instanceof

object instanceof constructor

Instanceof is very similar to Typeof. The instanceof operator is used to test whether constructor. Prototype exists on the prototype chain of the parameter Object. Unlike the Typeof method, the Instanceof method requires the developer to explicitly identify the object as a particular type.

Basic usage

// Define the constructor
function C(){} 
function D(){} 

var o = new C();


o instanceof C; // true because object.getProtoTypeof (o) === c.protoType


o instanceof D; // false, because d.prototype is not on o's prototype chain

o instanceof Object; / / true, because the Object. The prototype. IsPrototypeOf (o) returns true
C.prototype instanceof Object // true

C.prototype = {};
var o2 = new C();

o2 instanceof C; // true

o instanceof C; // false, c. protoType refers to an empty object that is not on o's prototype chain.

D.prototype = new C(); / / inheritance
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true because c. prototype is now on the prototype chain for O3
Copy the code

Above, is the basic use of instanceof, which determines whether an instance is an instanceof its parent or ancestor type.

console.log(Object instanceof Object);//true 
console.log(Function instanceof Function);//true 
console.log(Number instanceof Number);//false 
console.log(String instanceof String);//false 

console.log(Function instanceof Object);//true 

console.log(Foo instanceof Function);//true 
console.log(Foo instanceof Foo);//false
Copy the code

Why do Object and Function instanceof themselves equal true, but other classes instanceof themselves do not? How to explain?

To get to the bottom of instanceof, you need to look at two things: 1. How the operator is defined in the language specification. 2. JavaScript prototype inheritance mechanism.

The principle of the author

After the above analysis, we are not so unfamiliar with this kind of classic god chart, so let’s talk about instanceof with this picture

Here, I translate the specification definition directly into JavaScript code as follows:

function instance_of(L, R) {//L represents the left expression, R represents the right expression
 var O = R.prototype;// take the display prototype of R
 L = L.__proto__;// take the implicit prototype of L
 while (true) { 
   if (L === null) 
     return false; 
   if (O === L)// Return true if O is strictly L
     return true; L = L.__proto__; }}Copy the code

So with the above principle and the knowledge of prototypes explained above, let’s parse why Object and Function instanceof themselves are equal to true.

  • Object instanceof Object
// For convenience, first distinguish between left and right expressions
ObjectL = Object, ObjectR = Object; 
// Follow the rules step by step
O = ObjectR.prototype = Object.prototype 
L = ObjectL.__proto__ = Function.prototype 
// First judgmentO ! = L// loop to see if L still has __proto__
L = Function.prototype.__proto__ = Object.prototype 
// Second judgment
O == L 
/ / return true
Copy the code
  • Function instanceof Function
// For convenience, first distinguish between left and right expressions
FunctionL = Function, FunctionR = Function; 
// Follow the rules step by step
O = FunctionR.prototype = Function.prototype 
L = FunctionL.__proto__ = Function.prototype 
// First judgment
O == L 
/ / return true
Copy the code
  • Foo instanceof Foo
// For convenience, first distinguish between left and right expressions
FooL = Foo, FooR = Foo; 
// Follow the rules step by step
O = FooR.prototype = Foo.prototype 
L = FooL.__proto__ = Function.prototype 
// First judgmentO ! = L// Loop again to see if L still has __proto__
L = Function.prototype.__proto__ = Object.prototype 
// Second judgmentO ! = L// Loop again to see if L still has __proto__
L = Object.prototype.__proto__ = null 
// The third judgment
L == null 
/ / returns false
Copy the code

Inheritance implementation in ES5

In terms of inheritance implementation, Industrial Juda divided prototype inheritance into two categories, explicit inheritance and implicit inheritance, in his article on prototype. Those who are interested can click on the reference link at the end of the article.

This article, however, tries to explain the advantages and disadvantages of several common inheritance methods in an “informal” way. We can see a lot of comparison, in fact, the principle is the same, the noun is just the so-called term.

There are many books and blogs written about inheritance in great detail. The following inheritance methods are summarized in the book JavaScript Design Patterns. It’s an article I wrote three years ago.

The new keyword

Before we talk about inheritance, I think it’s important to talk about new

As an example, look at what the new keyword does

function Person(name,age){
  this.name = name;
  this.age = age;
  
  this.sex = 'male';
}

Person.prototype.isHandsome = true;

Person.prototype.sayName = function(){
  console.log(`Hello , my name is The ${this.name}`);
}

let handsomeBoy = new Person('Nealyang'.25);

console.log(handsomeBoy.name) // Nealyang
console.log(handsomeBoy.sex) // male
console.log(handsomeBoy.isHandsome) // true

handsomeBoy.sayName(); // Hello , my name is Nealyang
Copy the code

From the above example we can see:

  • Access to thePersonProperties in the constructor
  • Access to thePerson.prototypeThe properties in the

New Handwritten version one

function objectFactory() {

    const obj = new Object(),// Clone an Object from Object.prototype

    Constructor = [].shift.call(arguments);// Get the constructor passed in from the outside

    const F=function(){};
    F.prototype= Constructor.prototype;
    obj=new F();// Point to the correct prototype

    Constructor.apply(obj, arguments);// Use the constructor passed in from the outside to set properties for obj

    return obj;/ / returns the obj

};
Copy the code
  • withnew Object()Creates an object called obj
  • Take the first argument, which is the constructor we’re passing in. And because Shift modifies the array, soargumentsIt gets rid of the first argument
  • Point obj’s prototype to the constructor so that OBj can access the properties in the constructor prototype
  • useapplyChange the constructorthisObj points to the newly created object so that obj can access the properties in the constructor
  • Returns the obj

Let’s test it out:

function Person(name,age){
  this.name = name;
  this.age = age;
  
  this.sex = 'male';
}

Person.prototype.isHandsome = true;

Person.prototype.sayName = function(){
  console.log(`Hello , my name is The ${this.name}`);
}

function objectFactory() {

    let obj = new Object(),// Clone an Object from Object.prototype

    Constructor = [].shift.call(arguments);// Get the constructor passed in from the outside
    
    console.log({Constructor})

    const F=function(){};
    F.prototype= Constructor.prototype;
    obj=new F();// Point to the correct prototype

    Constructor.apply(obj, arguments);// Use the constructor passed in from the outside to set properties for obj

    return obj;/ / returns the obj

};

let handsomeBoy = objectFactory(Person,'Nealyang'.25);

console.log(handsomeBoy.name) // Nealyang
console.log(handsomeBoy.sex) // male
console.log(handsomeBoy.isHandsome) // true

handsomeBoy.sayName(); // Hello , my name is Nealyang
Copy the code

Note that we did not modify obj’s __proto__ implicit mount directly above.

New Handwritten version 2

Consider the case where the constructor returns a value:

  • If the constructor returns an object, we return that object as well
  • Otherwise, the default value is returned
function objectFactory() {

    var obj = new Object(),// Clone an Object from Object.prototype

    Constructor = [].shift.call(arguments);// Get the constructor passed in from the outside

    var F=function(){};
    F.prototype= Constructor.prototype;
    obj=new F();// Point to the correct prototype

    var ret = Constructor.apply(obj, arguments);// Use the constructor passed in from the outside to set properties for obj

    return typeof ret === 'object' ? ret : obj;// Make sure the constructor always returns an object

};
Copy the code

【THE LAST TIME】this: call, apply, bind, etc

Class type inheritance

function SuperClass() {
  this.superValue = true;
}
SuperClass.prototype.getSuperValue = function() {
  return this.superValue;
}

function SubClass() {
  this.subValue = false;
}
SubClass.prototype = new SuperClass();

SubClass.prototype.getSubValue = function() {
  return this.subValue;
}

var instance = new SubClass();

console.log(instance instanceof SuperClass)//true
console.log(instance instanceof SubClass)//true
console.log(SubClass instanceof SuperClass)//false
Copy the code

Console. log(subclass.prototype instanceof SuperClass)

While the implementation is clean and concise, this inheritance approach has two drawbacks:

  • If a common attribute of a parent class is a reference type, it will be shared by all instances in the subclass. Therefore, an instance of a subclass changes the common attribute inherited from the parent class constructor by its prototype, which will directly affect the other subclasses
  • Since the inheritance of a subclass is achieved by instantiating its prototype to the parent class, there is no way to pass parameters to the parent class when creating the parent class. Therefore, when instantiating the parent class, you cannot initialize the attributes in the parent constructor

Constructor inheritance

function SuperClass(id) {
  this.books = ['js'.'css'];
  this.id = id;
}
SuperClass.prototype.showBooks = function() {
  console.log(this.books);
}
function SubClass(id) {
  // Inherits the parent class
  SuperClass.call(this,id);
}
// Create the first instance of the subclass
var instance1 = new SubClass(10);
// Create the second subclass instance
var instance2 = new SubClass(11);

instance1.books.push('html');
console.log(instance1)
console.log(instance2)
instance1.showBooks();//TypeError
Copy the code

Superclass.call (this, ID) is, of course, the core statement that the constructor inherits. Since the parent class binds this to a property, subclasses naturally inherit the parent class’s common property. Due to this type of inheritance without involving the prototype prototype, so nature is not a subclass inherits the prototype of the parent class method, and if you want to be a subclass inherits, must be in the constructor, this created every instance alone have a not common, so it has violated the principles of code reuse, so integrated these two, We propose a combinatorial inheritance method

Combinatorial inheritance

function SuperClass(name) {
  this.name = name; 
  this.books = ['Js'.'CSS'];
}
SuperClass.prototype.getBooks = function() {
    console.log(this.books);
}
function SubClass(name,time) {
  SuperClass.call(this,name);
  this.time = time;
}
SubClass.prototype = new SuperClass();

SubClass.prototype.getTime = function() {
  console.log(this.time);
}
Copy the code

Above, we have solved some of the problems we discussed earlier, but is the code still a bit uncomfortable? At least this SuperClass constructor feels very out of place after being executed twice.

Primary inheritance

function inheritObject(o) {
    // Declare a transition object
  function F() {}// The prototype of the transition object inherits the parent object
  F.prototype = o;
  // Returns an instance of a transition object whose prototype inherits from the parent
  return new F();
}
Copy the code

The original type inherits the general implementation of the above, do you think of the implementation of our new keyword simulation?

In fact, this approach is very similar to class inheritance, he is just a encapsulation of class inheritance, where the transition object is equivalent to a subclass of class inheritance, but in the prototype inheritance as a common transition object, the purpose is to create a new instance object to return.

var book = {
    name:'js book'.likeBook: ['css Book'.'html book']}var newBook = inheritObject(book);
newBook.name = 'ajax book';
newBook.likeBook.push('react book');
var otherBook = inheritObject(book);
otherBook.name = 'canvas book';
otherBook.likeBook.push('node book');
console.log(newBook,otherBook);
Copy the code

As can be seen from the above code, primitive inheritance and class inheritance are the same. For variables that reference type, there are still instances of subclass sharing.

So, we also have the following parasitic successor

Parasitic inheritance

var book = {
    name:'js book'.likeBook: ['html book'.'css book']}function createBook(obj) {
    // Create new objects by prototyping
  var o = new inheritObject(obj);
  // Expand to new objects
  o.getName = function(name) {
    console.log(name)
  }
  // Returns the expanded new object
  return o;
}
Copy the code

In fact, parasitic inheritance is an extension of prototype inheritance, a process of secondary encapsulation, so that the newly created object not only has the attributes and methods of the parent class, but also adds other attributes and methods.

Parasitic combinatorial inheritance

Going back to combinatorial inheritance, we used a combination of class inheritance and constructor inheritance, but the problem was that the subclass was not an instance of the parent class, and the prototype of the subclass was an instance of the parent class, hence the parasitic combinatorial inheritance

Parasitic combinatorial inheritance is the combination of parasitic inheritance and constructor inheritance. But there’s something special about parasitic inheritance, where he’s dealing not with objects, but with prototypes of classes.

function inheritObject(o) {
  // Declare a transition object
  function F() {}// The prototype of the transition object inherits the parent object
  F.prototype = o;
  // Returns an instance of a transition object whose prototype inherits from the parent
  return new F();
}

function inheritPrototype(subClass,superClass) {
    // Copy a prototype copy of the parent class into the variable
  var p = inheritObject(superClass.prototype);
  // Fix the constructor property of a subclass being modified due to overriding the subclass's stereotype
  p.constructor = subClass;
  // Set the subclass prototype
  subClass.prototype = p;
}
Copy the code

In combinatorial inheritance, there is no problem inheriting properties and methods through constructors, so here we will focus on reinheriting a parent class through parasitic inheritance.

All we need to inherit is the parent’s prototype, without calling the parent’s constructor. In other words, in constructor inheritance, we have called the parent class’s constructor. So all we need is a copy of the superclass’s prototype object, which we can get via stereotype inheritance, but assigning it to a subClass would be problematic because the constructor property in p does not point to the subClass object. Therefore, in parasitic inheritance, an enhancement is made to the duplicate object P to fix the incorrect directivity of the constructor attribute. Finally, the resulting duplicate object P is assigned to the subclass prototype, so that the subclass prototype inherits the parent class and does not execute the parent class’s constructor.

function SuperClass(name) {
  this.name = name;
  this.books=['js book'.'css book'];
}
SuperClass.prototype.getName = function() {
  console.log(this.name);
}
function SubClass(name,time) {
  SuperClass.call(this,name);
  this.time = time;
}
inheritPrototype(SubClass,SuperClass);
SubClass.prototype.getTime = function() {
  console.log(this.time);
}
var instance1 = new SubClass('React'.'2017/11/11')
var instance2 = new SubClass('Js'.'2018/22/33');

instance1.books.push('test book');

console.log(instance1.books,instance2.books);
instance2.getName();
instance2.getTime();
Copy the code

This method of inheritance is actually shown in the figure above. One of the biggest changes is that the handling in the subclass prototype is given a reference in the parent class prototype, which is an object, so one thing you need to be aware of is that the subclass must go through Prototype to add the prototype method. Otherwise, assigning an object directly overwrites the object inherited from its parent class prototype.

The implementation principle of ES6 class

Some basic usage and introduction of ES6 class are not covered in this article due to space limitations. In this section, we’ll look at some of the implementations of parsing es6 syntax sugars including inheritance, mainly through Babel’s REPL.

The base class

We’re going to be rubbing back and forth with this class. The compiled code is then analyzed.

"use strict";

function _instanceof(left, right) {
  if( right ! =null &&
    typeof Symbol! = ="undefined" &&
    right[Symbol.hasInstance]
  ) {
    return!!!!! right[Symbol.hasInstance](left);
  } else {
    return left instanceofright; }}function _classCallCheck(instance, Constructor) {
  if(! _instanceof(instance, Constructor)) {throw new TypeError("Cannot call a class as a function"); }}var Person = function Person(name) {
  _classCallCheck(this, Person);

  this.name = name;
};
Copy the code

_instanceof is used to determine instance relationships. The code is simpler, and the _classCallCheck checks if the Person class is called using the new keyword. After all, when compiled into ES5, function can be called directly, but if called directly, this refers to the window object and throws Error.

Add attributes

"use strict";

function _instanceof(left, right) {... }function _classCallCheck(instance, Constructor) {... }function _defineProperty(obj, key, value) {
    if (key in obj) {
        Object.defineProperty(obj, key, {
            value: value,
            enumerable: true.configurable: true.writable: true
        });
    } else {
        obj[key] = value;
    }
    return obj;
}

var Person = function Person(name) {
    _classCallCheck(this, Person);

    _defineProperty(this."shili".'Instance Properties');

    this.name = name;
};

_defineProperty(Person, "jingtai".'Static properties');
Copy the code

It’s just a matter of who the attribute is assigned to. Assign directly to this if it is an instance property, or to the class if it is static. _defineProperty is just to check if the attribute name is duplicate.

Add methods

"use strict";

function _instanceof(left, right) {... }function _classCallCheck(instance, Constructor) {... }function _defineProperty(obj, key, value) {... }function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor); }}function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
}

var Person =
    /*#__PURE__*/
    function () {
        function Person(name) {
            _classCallCheck(this, Person);

            _defineProperty(this."shili".'Instance Properties');

            this.name = name;
        }

        _createClass(Person, [{
            key: "sayName".value: function sayName() {
                return this.name; }}, {key: "name".get: function get() {
                return 'Nealyang';
            },
            set: function set(newName) {
                console.log('new name is :'+ newName); }}], [{key: "eat".value: function eat() {
                return 'eat food'; }}]);returnPerson; } (); _defineProperty(Person,"jingtai".'Static properties');
Copy the code

It looks like a lot of code, but it’s just a _createClass function and a _defineProperties function.

The first is the constructor, the second is the array of functions to be added to the stereotype, and the third is the array of functions to be added to the class itself. And what this function does is very simple. To strengthen a constructor is to add functions to the constructor or its prototype.

And _defineProperties is multiple _defineProperties (which feels like crap, but it is). The default Enumerable is false and signals is true.

In fact, the above is the realization principle of ES6 class.

The extend keyword

"use strict";

function _instanceof(left, right) {... }function _classCallCheck(instance, Constructor) {... }var Parent = function Parent(name) {... };function _typeof(obj) {
    if (typeof Symbol= = ="function" && typeof Symbol.iterator === "symbol") {
        _typeof = function _typeof(obj) {
            return typeof obj;
        };
    } else {
        _typeof = function _typeof(obj) {
            return obj && typeof Symbol= = ="function" && obj.constructor === Symbol&& obj ! = =Symbol.prototype ? "symbol" : typeof obj;
        };
    }
    return _typeof(obj);
}

function _possibleConstructorReturn(self, call) {
    if (call && (_typeof(call) === "object" || typeof call === "function")) {
        return call;
    }
    return _assertThisInitialized(self);
}

function _assertThisInitialized(self) {
    if (self === void 0) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return self;
}

function _getPrototypeOf(o) {
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _getPrototypeOf(o);
}

function _inherits(subClass, superClass) {
    if (typeofsuperClass ! = ="function"&& superClass ! = =null) {
        throw new TypeError("Super expression must either be null or a function");
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            writable: true.configurable: true}});if (superClass) _setPrototypeOf(subClass, superClass);
}

function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
    };
    return _setPrototypeOf(o, p);
}

var Child =
    /*#__PURE__*/
    function (_Parent) {
        _inherits(Child, _Parent);

        function Child(name, age) {
            var _this;

            _classCallCheck(this, Child);

            _this = _possibleConstructorReturn(this, _getPrototypeOf(Child).call(this, name)); // Call parent class constructor(name)

            _this.age = age;
            return _this;
        }

        return Child;
    }(Parent);

var child1 = new Child('Full stack Front End Selection'.'0.3');
console.log(child1);
Copy the code

Cut out the class-related code generation, and all that’s left is inherited syntactic sugar parsing. The super keyword represents the constructor of the Parent class, which is equivalent to the Parent. Call (this) of ES5, and the implementation of this integration looks very similar to the parasitic combinative inheritance we talked about above.

In ES6 classes, subclasses must call the super method from the constructor method or they will get an error when creating a new instance. This is because the subclass does not have its own This object, but inherits the parent’s This object and then processes it. If you don’t call super, your subclasses don’t get this.

It is for this reason that in the constructor of a subclass, the this keyword can only be used after super is called, otherwise an error will be reported.

For the schematic diagram of prototype chain in ES6, please refer to the following schematic diagram:

With respect to the EXTEND keyword in ES6, the above code is entirely executable. There are only two lines of code that are important:

 _inherits(Child, _Parent);
  _this = _possibleConstructorReturn(this, _getPrototypeOf(Child).call(this, name)); 
Copy the code

Let’s analyze the specific implementation separately:

_inherits

The code is relatively simple, which is the content mentioned above, which is to establish the prototype chain relationship of Child and Parent. The code explanation has been noted in the code

function _inherits(subClass, superClass) {
    if (typeofsuperClass ! = ="function"&& superClass ! = =null) {// Determine the subClass type
        throw new TypeError("Super expression must either be null or a function");
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {// object.create The second argument adds the constructor attribute to subclass. prototype
            value: subClass,
            writable: true.configurable: true// Note that enumerable is not named; the default is false, which means constructor is unenumerable.}});if (superClass) _setPrototypeOf(subClass, superClass);
}

function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
    };
    return _setPrototypeOf(o, p);
}
Copy the code

_possibleConstructorReturn

_this = _possibleConstructorReturn(this, _getPrototypeOf(Child).call(this, name));
Copy the code

According to the es6 prototype diagram we sorted out above:

Child.prototype === Parent
Copy the code

So the above code can be translated as:

_this = _possibleConstructorReturn(this, Parent.call(this, name));
Copy the code

Then we dial the source code layer by layer implementation

function _possibleConstructorReturn(self, call) {
    if (call && (_typeof(call) === "object" || typeof call === "function")) {
        return call;
    }
    return _assertThisInitialized(self);
}

function _assertThisInitialized(self) {
    if (self === void 0) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return self;
}
Copy the code

Self is the same as this in the function new call returned by the Child’s IIFE.

Parent. Call (this,name) : Parent. Call (this,name)

As you can see, when we write the Parent constructor this way

class Parent {
    constructor(name) {
        this.name = name; }}Copy the code

So in the end, is passed to the second parameter _possibleConstructorReturn function call is an undefined. So call in _possibleConstructorReturn function will be judgment, return the correct this point to: a Child.

So the whole purpose of the code is to determine the initial value of the subclass constructor this, _this, based on the return type of the Parent constructor.

The last

【THE LAST TIME】 This series of articles on THE basics of JavaScript has been updated for three times. Let’s finish with a classic interview question.

function Foo() {
  getName = function() {
    alert(1);
  };
  return this;
}
Foo.getName = function() {
  alert(2);
};
Foo.prototype.getName = function() {
  alert(3);
};
var getName = function() {
  alert(4);
};
function getName() {
  alert(5);
}

// Write the following output:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
Copy the code

Leave your thoughts in the comments section

Study and communication

Pay attention to the public number: [full stack front selection] irregularly get good articles recommended and original articles.

Reply [1] in the public account, join the learning group of the full stack front end and communicate with each other.

reference

  • In-depth understanding of JavaScript prototypes
  • Inheritance and prototype chains
  • Proto__ __proto__ and constructor
  • An in-depth look at the JavaScript instanceof operator
  • JavaScript delves into the many ways to create objects, as well as the pros and cons
  • How does ES6 series Babel compile classes?
  • ES6 – Implementation principles for classes
  • Es6 classes and inheritance implementation principles
  • JavaScript deep new simulation implementation