[THE LAST TIME] all knowledge points related to JS prototype

Keywords: Javascript Attribute ECMAScript React

Preface

The last time, I have learned

[THE LAST TIME] has always been a series that I want to write, aiming to build up and review the front end.

It is also to check and fill in the gaps and share the technology.

Welcome to comment on Tucao.

A series of articles are first listed in the public address [full stack front end]. The author's collection is detailed in the GitHub address: Nealyang/personalBlog . Table of contents and order of issue are tentative

First of all, I would like to say that the content of the [THE LAST TIME] series has always included but not limited to the scope of the title.

Come back to the prototype, the commonplace question. But in many skilled workers, it seems that the relationship between Function and Object, prototype and "proto" is not clear. In this paper, we will introduce JavaScript inheritance systematically from prototype to implementation of es6 syntax sugar. If you can answer the following questions, then you don't need to spend any time reading this article.~

  • Why does typeof determine that null is an Object type?
  • What is the relationship between Function and Object?
  • What does the new keyword do? Handwritten implementation.
  • What is the relationship between prototype and proto? When is it equal?
  • There are several ways to implement inheritance in ES5. What are the advantages and disadvantages
  • How to implement a class in ES6
  • What is the implementation principle of ES6 extends keyword

If you have some doubts about the above questions, then...

THE LAST TIME series review

Catalog

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

Pay attention to the questions at the end of the article.~~

  • Prototype a shuttle

    • Function object and common object
    • __proto__
    • prototype
    • constructor
  • On the principle of type of & & instance of

    • typeof basic usage
    • On the principle of type of
    • instanceof basic usage
    • On the principle of instanceof
  • Inheritance implementation in ES5

    • new keyword

      • new handwritten version 1
      • new handwritten version 2
    • Class inheritance
    • Constructor inheritance
    • Combinatorial inheritance
    • Archetypal inheritance
    • Parasitic inheritance
    • Parasitic combinatorial inheritance
  • Implementation principle of class ES6

    • Foundation class
    • Add attribute
    • Adding method
    • extend keyword

      • _inherits
      • _possibleConstructorReturn

Prototype a shuttle

This is. It's the most basic that no one refutes. It's the most basic that no one refutes. It's the most basic that many people haven't sorted out and no one refutes. OK ~ why are there so many articles that you haven't figured out yet?

Before the concept is sorted out, we still put an old-fashioned so-called classic map:

  • function Foo is a method, such as Array and String built in JavaScript.
  • function Object is an Object
  • function Function is function
  • All of the above are functions, so. proto is Function.prototype.
  • Again, String, Array, Number, function and Object are all functions.

Old iron, if this picture is very clear, you can skip this chapter.

Old rules, let's sort out the concept directly.

Function object and common object

As the old saying goes, everything is an object. We all know that in JavaScript, there are several ways to create an object, such as the literal value of the object, or directly through the constructor new an object:

Let's ignore the significance of the above code for now. At least, we can see that they are all objects, but there are differences.

In fact, in JavaScript, we divide objects into Function objects and ordinary objects. The so-called Function Object is actually the class implementation of JavaScript that is simulated by Function. Objects and functions in JavaScript are typical Function objects.

The most intuitive feeling about function objects and ordinary objects is... Let's look at the code:

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

const obj1 = {};
const obj2 = new Object();
const obj3 = new fun1();
const obj4 = new new Function();


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

I don't know if you have any doubts about the above code ~ don't worry, let's sort it out bit by bit.

In the above code, obg1, obj2, obb3 and obf4 are common objects, and fun1, fun2 and fun3 are instances of functions, that is, Function objects.

Therefore, it can be seen that all Function instances are Function objects, and others are ordinary objects, including Function instances.

Everything in JavaScript is an object, and the object comes from the construction (constructor).

In the figure above, you wonder whether Function is related to new Function. It's like this:

Function.__proto__ === Function.prototype//true

__proto__

First of all, we need to be clear about two points: 1. proto and constructor are unique to objects. 2. The prototype property is unique to the function.

But in JavaScript, functions are also objects, so they also have the properties "proto" and "constructor".

In combination with the relationship between the Object and Function described above, take a look at the code and diagram.

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

Before combing the above relationship, let's talk about "proto".

__The example of proto_uuu, which is more complicated, can be said to be a historical problem.

The ECMAScript specification describes that prototype is an implicit reference, but some previous browsers have implemented the property "proto" privately, so that they can access the prototype defined as an implicit property through the explicit property of obj.

Therefore, the ECMAScript specification states that prototype should be an implicit reference:

  • Indirectly access the prototype object of the specified object through Object.getPrototypeOf(obj)
  • Indirectly set the prototype object of the specified object through object.setprototypeof (obj, another obj)
  • Some browsers have opened the "proto" interface in advance, so that the prototypes can be accessed directly through obj. Proto can be set directly through obj.
  • The ECMAScript 2015 specification has to bow to the facts and include the "proto" attribute as part of the specification

From the printing results of the browser, we can see that the object a in the figure above has a "proto" attribute. In fact, it's just a virtual node that developers can view the prototype intentionally. Although we can view it, it does not exist on the object.

__The proto_uuattribute cannot be traversed by for in or found by Object.keys(obj).

To access the obj. Proto property of an object, the get/set method of the proto property on the Object.prototype object is used by default.

Object.defineProperty(Object.prototype,'__proto__',{
    get(){
        console.log('get')
    }
});

({}).__proto__;
console.log((new Object()).__proto__);

For a more in-depth introduction of "proto", please refer to the article "in depth understanding of JavaScript prototype" by the industrial tycoon.

What we need to know here is that "proto" is unique to an object, and "proto" is an object pointing to another object, that is, its prototype object. We can also understand it as a parent class object. Its function is to go back to the object (parent object) pointed to by its proto property if the property does not exist inside the object when you access an object property. If the parent object still does not have this property, then go back to the parent class pointed to by its proto property. And so on, until null is found. And this search process, we often say that 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 itself is an object, just used to undertake a certain function.

All objects can be used as a prototype of another object.

Modify the diagram of "proto". We add prototype, which is unique to the function. Its purpose is to include properties and methods that can be shared for all instances of a specific type. Its meaning is the distant object of the function, that is, the distant object of the instance created by the function, as shown in the figure above: nealyang. When any function is created, the prototype attribute will be added to the function by default.

constructor

The constructor property is also unique to an object. It is an object pointing to a function, which is the constructor of the object.

Note that each object has its own constructor, either by itself or by inheritance. From the constructor property alone, only prototype objects are available. When each function is created, JavaScript will create a prototype object corresponding to the function at the same time, and the object created by the function. Prototype, the function. prototype.constructor = = the function itself, so even if the object created by the function has no constructor property, it can also find the corresponding constructor through proto. Object can eventually find its corresponding constructor.

The only special possibility is the one I left out at the beginning. The ancestor of JavaScript prototype: Function. It is its own constructor. So Function. Prototype = = = Function.

For intuitive understanding, we continue to add constructor s in the above figure:

Among them, the constructor property and dotted line represent the inherited constructor property.

__The prototype chain introduced by proto? Is as follows when we mark it intuitively in the figure.

Type of & & instanceof principle

What's a good way to say that prototype and inheritance are related to the principle of type judgment? After all, there is a little bit of connection in principle, and often the interview is also from the shallow to the deep, along the way out of the whole knowledge. So let's talk about it briefly here.

typeof

MDN documents click here: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/typeof

Basic Usage

The usage of typeof must be familiar to everyone. It is generally used to judge the type of a variable. We can use typeof to judge seven data types: number, undefined, symbol, string, function, boolean, and object. Unfortunately, typeof is a little embarrassed when judging the object type. It doesn't tell you exactly what kind of object the object belongs to.

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

Principle analysis

To understand why typeof judges null as object, we need to talk about how to store variable types at the bottom of js. Although, this is a bug in JavaScript Design.

In the original implementation of JavaScript, the value in JavaScript is represented by a label representing the type and the actual data value. The type label of the object is 0. Because null represents a null pointer (0x00 in most platforms), the type label of null is 0, and typeof null also returns "object". There was an ECMAScript fix proposal (by opt in), but it was rejected. The proposal will result in typeof null === 'null'.

When js stores variables in the bottom layer, it will store their type information in the lower 1-3 bits of the variable's machine code:

  • 1: integer
  • 110: Boolean
  • 100: String
  • 010: floating point
  • 000: objects

However, for undefined and null, the information storage of these two values is a little special:

  • null: all machine codes are 0
  • undefined: expressed as − 2 ^ 30 integer

So when using typeof to judge variable type, we need to pay attention to that it is better to use typeof to judge basic data type (including symbol) to avoid null judgment.

typeof is just an additional discussion area of instanceof brought out by the prototype.

instanceof

object instanceof constructor

Instanceof and typeof are very similar. The instanceof operator is used to detect whether the constructor.prototype exists in the prototype chain of the parameter object. Unlike the typeof method, the instanceof method requires the developer to explicitly confirm that the object is of a specific type.

Basic Usage

// Define 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 the prototype chain of o

o instanceof Object; // True, because Object.prototype.isPrototypeOf(o) returns true
C.prototype instanceof Object // true as above

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

o2 instanceof C; // true

o instanceof C; // false, C.prototype points to an empty object, which is not in the prototype chain of o.

D.prototype = new C(); // inherit
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true because C.prototype is now on the prototype chain of o3

As above, it is the basic use of instanceof, which can judge whether an instance is an instance of its parent type 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

Why is Object and Function instanceof equal to true, while instanceof other classes does not equal true? How to explain?

To understand the mystery of instanceof fundamentally, we need to start from two aspects: 1. How to define this operator in the language specification. 2. JavaScript prototype inheritance mechanism.

Principle analysis

After the above analysis, compared with this kind of classic map, it's not so strange. Let's talk about instanceof in this picture.

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

function instance_of(L, R) {//L for left expression, R for 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)// Here's the point: when O is strictly equal to L, return true 
     return true; 
   L = L.__proto__; 
 } 
}

Therefore, based on the above principles and the prototype related knowledge explained above, we will analyze why Object and Function instanceof are equal to true.

  • Object instanceof Object
// To facilitate expression, first distinguish the left expression and the right expression
ObjectL = Object, ObjectR = Object; 
// The following is a step-by-step deduction according to the specifications
O = ObjectR.prototype = Object.prototype 
L = ObjectL.__proto__ = Function.prototype 
// First judgment
O != L 
// Loop to find if L still has "proto"__ 
L = Function.prototype.__proto__ = Object.prototype 
// Second judgment
O == L 
// Return to true
  • Function instanceof Function
// To facilitate expression, first distinguish the left expression and the right expression
FunctionL = Function, FunctionR = Function; 
// The following is a step-by-step deduction according to the specifications
O = FunctionR.prototype = Function.prototype 
L = FunctionL.__proto__ = Function.prototype 
// First judgment
O == L 
// Return to true
  • Foo instanceof Foo
// To facilitate expression, first distinguish the left expression and the right expression
FooL = Foo, FooR = Foo; 
// The following is a step-by-step deduction according to the specifications
O = FooR.prototype = Foo.prototype 
L = FooL.__proto__ = Function.prototype 
// First judgment
O != L 
// Loop again to find if L still has "proto"__ 
L = Function.prototype.__proto__ = Object.prototype 
// Second judgment
O != L 
// Loop again to find if L still has "proto"__ 
L = Object.prototype.__proto__ = null 
// Third judgment
L == null 
// Return to false

Inheritance implementation in ES5

In the aspect of inheritance implementation, in his prototype article, Gongju divides prototype inheritance into two categories: explicit inheritance and implicit inheritance. Interested parties can click the reference link at the end of the article.

But this article still hopes to be able to explain several common inheritance methods and advantages and disadvantages based on the "popular" way. In fact, the principle is the same. Nouns are just so-called pronouns.

There are many books and blogs about inheritance. The following inheritance methods are summarized in the book "JavaScript Design Pattern". It's also an article that I wrote three years ago.

new keyword

Before explaining inheritance, I think it's necessary to introduce new.~

An example to see 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 ${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

From the above example, we can see:

  • Access to properties in the Person constructor
  • Access to properties in Person.prototype

new handwritten version 1

function objectFactory() {

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

    Constructor = [].shift.call(arguments);//Get external incoming constructors

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

    Constructor.apply(obj, arguments);//Setting properties for obj by borrowing an external incoming constructor

    return obj;//Return to obj

};
  • A new object obj is created in the way of new Object().
  • Take out the first parameter, which is the constructor we want to pass in. In addition, because shift will modify the original array, arguments will be removed from the first argument
  • Point the prototype of obj to the constructor so that OBJ can access the properties in the constructor prototype
  • Using apply, change the pointer of this constructor to the new object, so that obj can access the properties in the constructor
  • Return to obj

Let's test it:

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 ${this.name}`);
}

function objectFactory() {

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

    Constructor = [].shift.call(arguments);//Get external incoming constructors
    
    console.log({Constructor})

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

    Constructor.apply(obj, arguments);//Setting properties for obj by borrowing an external incoming constructor

    return obj;//Return to 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

Note that we did not directly modify the implicit mount of obj.

new handwritten version 2

Consider the case where the constructor returns a value:

  • If the constructor returns an object, then we also return this object
  • Otherwise, the default value will be returned.
function objectFactory() {

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

    Constructor = [].shift.call(arguments);//Get external incoming constructors

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

    var ret = Constructor.apply(obj, arguments);//Setting properties for obj by borrowing an external incoming constructor

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

};

Explanation on the usage and principle of call, apply, bind, this, etc. [THE LAST TIME]this: call,apply,bind

Class 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

From the principle of instanceof we introduced earlier, we know that the third console returns true console.log(SubClass.prototype instanceof SuperClass) if it is written like this.

Although the implementation is clear and concise, there are two disadvantages of this inheritance method:

  • Because the subclass instantiates the parent class through its prototype prototype prototype and inherits the parent class, if the common property in the parent class is a reference type, it will be shared by all instances in the subclass. Therefore, if the instance of a subclass changes the common property inherited by the subclass prototype from the parent constructor, it will directly affect other subclasses.
  • Because the inheritance of subclass implementation is realized by instantiating the parent class with its prototype prototype prototype, it is impossible to pass parameters to the parent class when creating the parent class. Therefore, when the parent class is instantiated, the properties in the parent class constructor cannot be initialized.

Constructor inheritance

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

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

SuperClass.call(this,id) is of course the core statement of constructor inheritance. Since this is bound to the parent class, the child class naturally inherits the common properties of the parent class. Since prototype prototype is not involved in this type of inheritance, the prototype method of the parent class will not be inherited by the subclass naturally. If you want to be inherited by the subclass, you must put it into the constructor, so that each instance created will have a separate copy and cannot be used together, which violates the principle of code reuse. Therefore, combining the above two, we propose a group. Syntagmatic 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);
}

As mentioned above, we have solved some problems mentioned before, but is it from the code point of view, or some discomfort? At least this SuperClass constructor has been executed twice and it feels very inappropriate.

Archetypal 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 the parent object
  return new F();
}

The implementation of prototype inheritance is as above. Did you think of the implementation of our new keyword simulation?

In fact, this method is very similar to class inheritance. It is just a encapsulation of class inheritance, in which the transition object is equivalent to a subclass of class inheritance, but it exists as a common transition object in prototype inheritance, in order to create a new instance object to be returned.

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);

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

So, we have the following parasitic relay

Parasitic inheritance

var book = {
    name:'js book',
    likeBook:['html book','css book']
}
function createBook(obj) {
    //Create new objects in prototype mode
  var o = new inheritObject(obj);
  // Expand new objects
  o.getName = function(name) {
    console.log(name)
  }
  // Return to the new object after expansion
  return o;
}

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

Parasitic combinatorial inheritance

Back to the previous combinatorial inheritance, we used the combination of class inheritance and constructor inheritance at that time, but the problem is that the subclass is not an instance of the parent class, and the prototype of the subclass is an instance of the parent class, so we have the parasitic combinatorial inheritance.

The parasitic combinatorial inheritance is a combination of the parasitic inheritance and the constructor inheritance. But there is something special about parasitic inheritance. It deals with not objects, but 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 the parent object
  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 the subclass was modified due to overriding the prototype of the subclass
  p.constructor = subClass;
  // Set subclass prototype
  subClass.prototype = p;
}

In combinatorial inheritance, there is no problem with the properties and methods inherited through constructors, so here we mainly explore the prototype of inheriting the parent class again through parasitic inheritance.

We need to inherit only the prototype of the parent class, without calling the constructor of the parent class. In other words, in constructor inheritance, we have called the constructor of the parent class. So what we need is a copy of the prototype object of the parent class, which we can get through prototype inheritance, but it will be a problem to assign it directly to the child class. Because the constructor property in the replica object P copied from the prototype object of the parent class points to the child class object, so we need to enhance the replica object P in parasitic inheritance. Fix the incorrect directivity of the constructor property, and finally assign the copied object p to the subClass prototype, so that the subClass prototype inherits the prototype of the parent class and does not execute the constructor of the parent class.

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();

This way of inheritance is actually shown in the figure above. The biggest change is the processing in the subclass prototype, which is given a reference in the parent prototype. This is an object. Therefore, one thing you need to pay attention to is that the subclass's method to add the prototype must be added through prototype. Otherwise, the object directly assigned will overwrite the object inherited from the parent prototype.

Implementation principle of class ES6

As for the basic usage and introduction of class in ES6, this article will not introduce it for the limited space. In this chapter, we mainly adopt REPL of babel To see and analyze some of the implementations of each syntax sugar including inheritance in es6.

Foundation class

We'll rub back and forth according to this class. Then analyze the compiled code.

"use strict";

function _instanceof(left, right) {
  if (
    right != null &&
    typeof Symbol !== "undefined" &&
    right[Symbol.hasInstance]
  ) {
    return !!right[Symbol.hasInstance](left);
  } else {
    return left instanceof right;
  }
}

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;
};

_instanceof is to judge the instance relationship. The above code is relatively simple. The function of ﹐ classCallCheck is to check whether the Person class is called through the new keyword. After all, after being compiled into ES5, function can be called directly, but if it is called directly, this will point to the window object and Throw Error.

Add attribute

"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 attribute');

    this.name = name;
};

_defineProperty(Person, "jingtai", ' Static attribute');

In fact, it's about who the attribute is assigned to. If it is an instance property, assign it directly to this. If it is a static property, assign it to a class. _Define property is to determine whether the property name is duplicate.

Adding method

"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 attribute');

            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';
            }
        }]);

        return Person;
    }();

_defineProperty(Person, "jingtai", ' Static attribute');

There seems to be a lot of code, but it's just a function of "createClass" and "defineProperties".

First, look at the three parameters of the function "createClass". The first is the constructor, the second is the function array to be added to the prototype, and the third is the function array to be added to the class itself. In fact, the function is very simple. Strengthen the constructor. The so-called strengthen constructor is to add some functions to the constructor or its prototype.

And "defineProperties" are multiple "defineProperties" (it feels like nonsense, but it does). The default enumerable is false, and the configurable is true.

In fact, the above is the implementation principle of es6 class.

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 (typeof superClass !== "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 constructor(name) of parent class

            _this.age = age;
            return _this;
        }

        return Child;
    }(Parent);

var child1 = new Child('Full stack front end selection', '0.3');
console.log(child1);

Delete the class related code generation, and the rest is the inherited syntax sugar analysis. Among them, the super keyword represents the constructor of the parent class, which is equivalent to the Parent.call(this) of ES5. Then, according to the inheritance method mentioned above, do you feel that the implementation of this integration is very similar to the parasitic combinatorial inheritance?

In ES6 class, the subclass must call the super method in the constructor method, otherwise the new instance will report the error. This is because the subclass does not have its own this object, but inherits the parent's this object and processes it. If the super method is not called, the subclass will not get this object.

For this reason, in the constructor of a subclass, you can only use this keyword after calling super, otherwise an error will be reported.

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

As for the extend keyword in ES6, we can see the above code completely according to the execution. In fact, the key code is just two lines:

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

We will analyze the following specific implementation:

_inherits

The code is relatively simple, which is all mentioned above. It is to establish the prototype chain relationship between Child and Parent. Code interpretation noted in code

function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {//subClass type judgment
        throw new TypeError("Super expression must either be null or a function");
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {//The second parameter of Object.create is to add constructor property to subClass.prototype.
            value: subClass,
            writable: true,
            configurable: true//Note that the enumerable is not named here, and the default is false, which means the constructor is not enumerable.
        }
    });
    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);
}

_possibleConstructorReturn

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

According to the es6 prototype we arranged in the figure above, we can see that:

Child.prototype === Parent

So the above code can be translated into:

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

And then we call the source code layer by layer.

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;
}

The above code, self, is actually this called by function new returned by Child's IIFE. The printed result is as follows:

There may be some doubts about Parent.call(this,name). It doesn't matter. We can debug it under Chrome.

As you can see, when our Parent's constructor says

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

In the end, the second parameter call passed to the ﹐ possibleConstructorReturn function is an undefined. So the call will be judged in the ﹣ possibleConstructorReturn function, and the correct direction of this will be returned: Child.

So the purpose of the whole code is to determine the initial value "this" of the subclass constructor according to the return value type of the Parent constructor.

Last

[THE LAST TIME] series of articles on JavaScript foundation are currently updated three times. Let's have another classic interview question at the end!

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();

Old fellow, leave your thoughts in the comment area.

Exchange of learning

Pay attention to the public address: full stack front end selection.

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

Reference

Posted by the_manic_mouse on Wed, 30 Oct 2019 20:23:25 -0700