preface
Hi, I’m Wakawa. This is the fifth part of the interviewers’ questions series, which aims to help readers improve the basic knowledge of JS, including new, call, apply, this, inheritance related knowledge.
The interviewer asked the following article series: Interested readers can click here to read it.
Q: Can you simulate the JS new operator? Q: can you simulate the JS call and apply methods? Interviewer: JS inheritance
As those of you who have used React know, the extends inheritance of React.Component is often used.
Function Component(props, context, updater) {//... } Component.prototype.setState = function(partialState, callback){ // ... } const React = { Component, // ... } class index extends react.ponent {//... }Copy the code
Check out the React Github source code here
The interviewer can follow this up by asking questions about JS inheritance, such as how ES6 class inheritance is implemented with ES5. It is said that many people did not answer well.
Relationships between constructors, prototype objects, and instances
To understand the extends inheritance, let’s review the relationship between constructors, prototype objects, and instances. The code represents:
function F(){} var f = new F(); // constructor. Constructor === F; // true F.__proto__ === Function.prototype; // true Function.prototype.__proto__ === Object.prototype; // true Object.prototype.__proto__ === null; F. __proto__ === f.protototype; // true F.prototype.__proto__ === Object.prototype; // true Object.prototype.__proto__ === null; // trueCopy the code
The author drew a picture to show:
ES6 extends
What does inheritance do
Let’s first look at the ES6 inheritance code that contains static methods:
// ES6
class Parent{
constructor(name){
this.name = name;
}
static sayHello(){
console.log('hello');
}
sayName(){
console.log('my name is ' + this.name);
return this.name;
}
}
class Child extends Parent{
constructor(name, age){
super(name);
this.age = age;
}
sayAge(){
console.log('my age is ' + this.age);
return this.age;
}
}
let parent = new Parent('Parent');
let child = new Child('Child', 18);
console.log('parent: ', parent); // parent: Parent {name: "Parent"}
Parent.sayHello(); // hello
parent.sayName(); // my name is Parent
console.log('child: ', child); // child: Child {name: "Child", age: 18}
Child.sayHello(); // hello
child.sayName(); // my name is Child
child.sayAge(); // my age is 18
Copy the code
There are two prototype chains in this code, if you will see the actual code.
Child.__proto__ === Parent; // true Parent.__proto__ === Function.prototype; // true Function.prototype.__proto__ === Object.prototype; // true Object.prototype.__proto__ === null; __proto__ === child. Prototype; // true Child.prototype.__proto__ === Parent.prototype; // true Parent.prototype.__proto__ === Object.prototype; // true Object.prototype.__proto__ === null; // trueCopy the code
A picture is worth a thousand words. The author also drew a picture to show it, as shown in the figure:
Combine the code with the diagram.ES6 extends
Inheritance is basically:
-
- The subclass constructor (
Child
) (__proto__
) refers to the superclass constructor (Parent
),
- The subclass constructor (
-
- Subclass instances
child
The prototype object (Child.prototype
) (__proto__
) points to the superclassparent
The prototype object (Parent.prototype
).
- Subclass instances
These are the two lines that I’ve drawn in different colors.
-
- Subclass constructor
Child
Inherits from the superclass constructorParent
Properties of. usesuper
Call (ES5
To usecall
orapply
Call pass parameter).
- Subclass constructor
These are the two lines that I’ve drawn in different colors.
Those of you who have read section 6.3 inheritance in JavaScript Advanced Programming – Edition 3 should know that these 2 and 3 dots are parasitic combinational inheritance, and the examples in this book do not contain the first dot. The 1 and 2 dots are both relative to the __proto__ link set. The question is, what can be set to the __proto__ link?
new
,Object.create
andObject.setPrototypeOf
You can set the__proto__
Note that __proto__ is the browser manufacturer’s own implementation. The __proto__ of the new instance refers to the constructor’s prototype. This is what new does. Paraphrase a paragraph from an article I’ve written before. Interviewer: can you simulate the implementation of JS new operator, interested readers can click to view.
new
What was done:
- A completely new object is created.
- This object will be executed
[[Prototype]]
(i.e.__proto__
Links).- The generated new object is bound to the function call
this
.- through
new
Each object created will eventually be[[Prototype]]
Link to this functionprototype
On the object.- If the function does not return an object type
Object
(including,Functoin
.Array
.Date
.RegExg
.Error
), thennew
The function call in the expression automatically returns the new object.
Object.create
ES5 provides
The object.create (proto, [propertiesObject]) method creates a new Object, using an existing Object to provide the __proto__ of the newly created Object. It takes two arguments, but the second optional argument is the property descriptor (less commonly used; the default is undefined). For browsers that do not support ES5, the PloyFill scheme is provided on the MDN. MDN Object.create()
// Simplified version: this is the same principle that new sets the __proto__ link. if(typeof Object.create ! == 'function'){ Object.create = function(proto){ function F() {} F.prototype = proto; return new F(); }}Copy the code
Object.setPrototypeOf
Provide the ES6
Object.setPrototypeOf
MDN
The object.setPrototypeof () method sets the Prototype (that is, the internal [[Prototype]] property) of a specified Object to another Object or to null. Object.setPrototypeOf(obj, prototype)
'ployfill' // Works only for Chrome and FireFox, does not work in IE: Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) { obj.__proto__ = proto; return obj; }Copy the code
Nodejs source code makes use of this implementation inherited utility function. nodejs utils inherits
function inherits(ctor, superCtor) {
if (ctor === undefined || ctor === null)
throw new ERR_INVALID_ARG_TYPE('ctor', 'Function', ctor);
if (superCtor === undefined || superCtor === null)
throw new ERR_INVALID_ARG_TYPE('superCtor', 'Function', superCtor);
if (superCtor.prototype === undefined) {
throw new ERR_INVALID_ARG_TYPE('superCtor.prototype',
'Object', superCtor.prototype);
}
Object.defineProperty(ctor, 'super_', {
value: superCtor,
writable: true,
configurable: true
});
Object.setPrototypeOf(ctor.prototype, superCtor.prototype);
}
Copy the code
ES6
theextends
theES5
Version of the implementation
Now that we know what the ES6 extends inheritance does and how __proto__ is set, it’s easy to implement the ES6 extends inheritance example above in ES5.
Function Parent(name){this.name = name; } Parent.sayHello = function(){ console.log('hello'); } Parent.prototype.sayName = function(){ console.log('my name is ' + this.name); return this.name; } function Child(name, age){// equivalent to super parent-call (this, name); this.age = age; } // new function object(){ function F() {} F.prototype = proto; return new F(); } function _inherits(Child, Parent){ // Object.create Child.prototype = Object.create(Parent.prototype); // __proto__ // Child.prototype.__proto__ = Parent.prototype; Child.prototype.constructor = Child; // ES6 // Object.setPrototypeOf(Child, Parent); // __proto__ Child.__proto__ = Parent; } _inherits(Child, Parent); Child.prototype.sayAge = function(){ console.log('my age is ' + this.age); return this.age; } var parent = new Parent('Parent'); var child = new Child('Child', 18); console.log('parent: ', parent); // parent: Parent {name: "Parent"} Parent.sayHello(); // hello parent.sayName(); // my name is Parent console.log('child: ', child); // child: Child {name: "Child", age: 18} Child.sayHello(); // hello child.sayName(); // my name is Child child.sayAge(); // my age is 18Copy the code
We can see the above ES6 example through babeljs transcoding to ES5 for a more rigorous implementation.
// The converted code is briefly commented "use strict"; 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); } / / Parent _possibleConstructorReturn judgment. The call(this, name) function returns whether the value is null or a function or object. 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; } // Get __proto__ 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"); } // the object.create () method creates a new Object, using an existing Object to provide the __proto__ of the newly created Object. Subclass.prototype. __proto__ === superclass.prototype; Subclass.prototype = object.create (superClass && superclass.prototype, {constructor: {value:}) subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } // set __proto__ function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); 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"); }} 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, Constructor); staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } // ES6 var Parent = function () { function Parent(name) { _classCallCheck(this, Parent); this.name = name; } _createClass(Parent, [{ key: "sayName", value: function sayName() { console.log('my name is ' + this.name); return this.name; } }], [{ key: "sayHello", value: function sayHello() { console.log('hello'); } }]); return Parent; } (); var Child = function (_Parent) { _inherits(Child, _Parent); function Child(name, age) { var _this; _classCallCheck(this, Child); // Child.__proto__ => Parent // A conversion is super (name) / / _possibleConstructorReturn judgment Parent. Call (this name), or function return value is null or function object. _this = _possibleConstructorReturn(this, _getPrototypeOf(Child).call(this, name)); _this.age = age; return _this; } _createClass(Child, [{ key: "sayAge", value: function sayAge() { console.log('my age is ' + this.age); return this.age; } }]); return Child; }(Parent); var parent = new Parent('Parent'); var child = new Child('Child', 18); console.log('parent: ', parent); // parent: Parent {name: "Parent"} Parent.sayHello(); // hello parent.sayName(); // my name is Parent console.log('child: ', child); // child: Child {name: "Child", age: 18} Child.sayHello(); // hello child.sayName(); // my name is Child child.sayAge(); // my age is 18Copy the code
If you are not familiar with JS inheritance, please read the relevant chapters of the following books. You can find the corresponding PDF version by yourself.
It is recommended to read the JS inheritance related book chapters
“JavaScript advanced programming version 3” – chapter 6 object-oriented programming, six inheritance schemes, are prototype chain inheritance, borrowing constructor inheritance, combination inheritance, primitive type inheritance, parasitic inheritance, parasitic combinatorial inheritance. Turing community book address, after the release of github link, which contains several inherited code demo.
Object-oriented programming in JavaScript, version 2. – Chapter 6 inheritance, 12 inheritance scenarios. 1. Prototype chain (imitating tradition), 2. Inheriting only from prototype, 3. Prototype attribute copy method, 5. Full attribute copy method (shallow copy method), 6. Deep copy method, 7. Prototypical inheritance, 8. Extended and enhanced patterns, 9. Multiple inheritance, 10. Constructor borrowing 12. Constructor borrowing and attribute copying.
Introduction to ES6 Standards – Chapter 21 Inheritance of Class
In Depth ES6 – Chapter 9 Classes in JavaScript
“JavaScript you Didn’t know – Volume 1” Chapter 6 behavior delegation and Appendix A ES6 class
conclusion
Inheritance for JS is that the parent class has methods and properties, static methods, and so on, the child class must also have. In the subclass can use the prototype chain lookup, can also call the parent class in the subclass, or copy a copy from the parent class to the subclass and so on. There are many ways to inherit. The important thing is to understand and be familiar with how the objects, prototypes, and constructors work. The rest is simple. Parasitic combinatorial inheritance is one of the most commonly used by developers. Review parasitic combinatorial inheritance. There are three main points:
-
- Of the subclass constructor
__proto__
Points to the superclass constructor and inherits the static methods of the superclass
- Of the subclass constructor
-
- Of the subclass constructor
prototype
the__proto__
Pointing to the superclass constructorprototype
, which inherits methods from the parent class.
- Of the subclass constructor
-
- The subclass constructor calls the superclass constructor, inheriting the properties of the superclass.
At this point, the article is basically finished. Article code and images are included in github inhert and demo to show es6-extends. You can also use the Console and Source panels to view them.
Readers are welcome to comment on any problems or improvements they find. In addition, I think it is well written. I can like, comment and forward it, which is also a kind of support for the author.
Selected articles by the author
Learn how to use sentry source code and build their own front-end exception monitoring SDK. Learn how to use lodash source code and build their own functional programming library. Q: Can you simulate the js call and apply methods? Q: Can you simulate the js bind method? Q: Can you simulate the js bind method? Can simulate the implementation of the JS new operator front end using puppeteer crawler to generate the React. JS small book PDF and merge
about
Author: often with the name of Ruochuan mixed in rivers and lakes. The front road lovers | | PPT know little, only good study. Personal blog segmentfault front view column, opened front view column, welcome to pay attention to ~ gold column, welcome to pay attention to ~ github front view column, welcome to pay attention to ~ github blog, find a star^_^~
Wechat public account Ruochuan Vision
May be more interesting wechat public number, long press scan code concern. You can also add wechat Ruochuan12, indicate the source, pull you into [front view communication group].