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
- why
typeof
judgenull
是Object
Type? Function
和Object
What is the relationship?new
What 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
extends
What 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 new keyword
- 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:
- through
Object.getPrototypeOf(obj)
Indirectly accessing the specified objectprototype
object - through
Object.setPrototypeOf(obj, anotherObj)
Indirectly sets the specified objectprototype
object - Some browsers are open early
__proto__
To allow passage throughobj.__proto__
Direct access to the prototype, throughobj.__proto__ = anotherObj
Set 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 0undefined
: 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 the
Person
Properties in the constructor - Access to the
Person.prototype
The 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
- with
new Object()
Creates an object called obj - Take the first argument, which is the constructor we’re passing in. And because Shift modifies the array, so
arguments
It gets rid of the first argument - Point obj’s prototype to the constructor so that OBj can access the properties in the constructor prototype
- use
apply
Change the constructorthis
Obj 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