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 extendsWhat 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 extendsInheritance is basically:

    1. The subclass constructor (Child) (__proto__) refers to the superclass constructor (Parent),
    1. Subclass instanceschildThe prototype object (Child.prototype) (__proto__) points to the superclassparentThe prototype object (Parent.prototype).

These are the two lines that I’ve drawn in different colors.

    1. Subclass constructorChildInherits from the superclass constructorParentProperties of. usesuperCall (ES5To usecallorapplyCall pass parameter).

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.createandObject.setPrototypeOfYou 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.

newWhat was done:

  1. A completely new object is created.
  2. This object will be executed[[Prototype]](i.e.__proto__Links).
  3. The generated new object is bound to the function callthis.
  4. throughnewEach object created will eventually be[[Prototype]]Link to this functionprototypeOn the object.
  5. If the function does not return an object typeObject(including,Functoin.Array.Date.RegExg.Error), thennewThe 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

ES6theextendstheES5Version 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:

    1. Of the subclass constructor__proto__Points to the superclass constructor and inherits the static methods of the superclass
    1. Of the subclass constructorprototypethe__proto__Pointing to the superclass constructorprototype, which inherits methods from the parent class.
    1. 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].