Интервьюер спросил: наследование JS

JavaScript React.js

предисловие

Здравствуйте, яВакагава. Это пятая статья из серии «Вопросы интервьюера», призванной помочь читателям улучшитьJSбазовые знания, в том числеnew、call、apply、this、继承связанная информация.

面试官问系列Статья выглядит следующим образом: Заинтересованные читатели могут нажать, чтобы прочитать.

1.Интервьюер спросил: Могу ли я смоделировать новый оператор, реализующий JS?
2.Интервьюер спросил: Могу ли я смоделировать метод привязки, реализующий JS?
3.Интервьюер спросил: Могу ли я смоделировать вызов и применить методы JS?
4.Интервьюер спросил: этот пункт JS
5.Интервьюер спросил: наследование JS

использовалReactчитателей знают, что часто используютextendsнаследоватьReact.Component.

// 部分源码
function Component(props, context, updater) {
  // ...
}
Component.prototype.setState = function(partialState, callback){
    // ...
}
const React = {
    Component,
    // ...
}
// 使用
class index extends React.Component{
    // ...
}

Нажмите здесь, чтобы просмотреть исходный код React github

Интервьюер может задать этот вопросJSВопросы, связанные с наследством, такие как:ES6изclassКак реализовать наследование с помощью ES5. Говорят, что многие люди ответили плохо.

Связь между конструкторами, объектами-прототипами и экземплярами

Прежде чем понять расширение наследования, давайте рассмотрим отношения между конструкторами, объектами-прототипами и экземплярами. Код говорит:

function F(){}
var f = new F();
// 构造器
F.prototype.constructor === F; // true
F.__proto__ === Function.prototype; // true
Function.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true

// 实例
f.__proto__ === F.prototype; // true
F.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true

Автор нарисовал картинку, показывающую:构造函数-原型对象-实例关系图By@若川

ES6 extendsЧто делает наследование

Давайте сначала посмотрим на этот раздел, содержащий статические методы.ES6Унаследованный код:

// 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

В этом коде есть две цепочки прототипов, не верьте, чтобы увидеть конкретный код.

// 1、构造器原型链
Child.__proto__ === Parent; // true
Parent.__proto__ === Function.prototype; // true
Function.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true
// 2、实例原型链
child.__proto__ === Child.prototype; // true
Child.prototype.__proto__ === Parent.prototype; // true
Parent.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true

Картинка стоит тысячи слов, я также нарисовал картинку, чтобы представить ее, как показано на рисунке:

ES6继承(extends)关系图By@若川Сочетание кода и диаграмм может быть известно.ES6 extendsНаследование, в основном:

    1. Положите конструктор подкласса (Child)Прототип(__proto__) указывает на конструктор родительского класса (Parent),
    1. экземпляр подклассаchildОбъект-прототип (Child.prototype) Прототип(__proto__) указывает на родительский классparentОбъект-прототип (Parent.prototype).

Эти две точки также являются двумя линиями, отмеченными на рисунке разными цветами.

    1. конструктор подклассаChildУнаследовал конструктор родительского классаParentсвойства . использоватьsuperназывается (ES5затем используйтеcallилиapplyпараметр вызова).

То есть две линии отмечены на рисунке разными цветами.

Прочтите главу «Продвинутое программирование на JavaScript — 3-е издание».6.3继承читатели должны знать, что это2和3小点, точноПаразитическая композиционная наследственность, в книге нет примеров第1小点.1和2小点относятся к настройкам__proto__Связь. Вопрос в том, что можно установить__proto__Что насчет ссылки.

new,Object.createа такжеObject.setPrototypeOfможно установить__proto__

Объяснять,__proto__Этот способ записи является собственной реализацией производителя браузера. Давайте посмотрим на картинку и кодnew,new__proto__ исходящего экземпляра указывает на конструкторprototype,ЭтоnewСписок задач. Возьмите отрывок из предыдущей статьи.Интервьюер спросил: Могу ли я смоделировать новый оператор, реализующий JS?, заинтересованные читатели могут нажать для просмотра.

newЧто вы наделали:

  1. Создается новый объект.
  2. Этот объект будет выполнен[[Prototype]](то есть,__proto__)Связь.
  3. Результирующий новый объект привязывается к вызову функцииthis.
  4. пройти черезnewКаждый созданный объект в конечном итоге будет[[Prototype]]ссылка на эту функциюprototypeна объекте.
  5. Если функция не возвращает тип объектаObject(ВключатьFunctoin, Array, Date, RegExg, Error),ТакnewВызовы функций в выражениях автоматически возвращают этот новый объект.

Object.create ES5提供的

Object.create(proto, [propertiesObject])метод создает новый объект, используя существующий объект для предоставления __proto__ вновь созданного объекта. Он принимает два параметра, но вторым необязательным параметром является дескриптор атрибута (обычно не используется, по умолчаниюundefined). для не поддерживаемыхES5браузер,MDNпредоставляется наployfillстроить планы.MDN Object.create()

// 简版:也正是应用了new会设置__proto__链接的原理。
if(typeof Object.create !== 'function'){
    Object.create = function(proto){
        function F() {}
        F.prototype = proto;
        return new F();
    }
}

Object.setPrototypeOf ES6提供的

Object.setPrototypeOf MDN

Object.setPrototypeOf()Способ установки заданного прототипа объекта (т. е. внутреннего[[Prototype]]имущество) к другому объекту илиnull.Object.setPrototypeOf(obj, prototype)

`ployfill`
// 仅适用于Chrome和FireFox,在IE中不工作:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
  obj.__proto__ = proto;
  return obj; 
}

nodejsИсходный код должен использовать эту реализацию унаследованной полезной функции.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);
}

ES6изextendsизES5реализация версии

понялES6 extendsКакие операции и настройки наследуются__proto__После точки знаний поставьте вышеуказанноеES6использование примераES5легче реализовать, т.Реализация паразитного композиционного наследования, упрощенный код:

// ES5 实现ES6 extends的例子
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){
    // 相当于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 18

Мы можем поставить вышеES6的例子пройти черезbabeljsперекодировано вES5Посмотрим, более строгая реализация.

// 对转换后的代码进行了简要的注释
"use strict";
// 主要是对当前环境支持Symbol和不支持Symbol的typeof处理
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);
}
// _possibleConstructorReturn 判断Parent。call(this, name)函数返回值 是否为null或者函数或者对象。
function _possibleConstructorReturn(self, call) {
    if (call && (_typeof(call) === "object" || typeof call === "function")) {
        return call;
    }
    return _assertThisInitialized(self);
}
// 如何 self 是void 0 (undefined) 则报错
function _assertThisInitialized(self) {
    if (self === void 0) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return self;
}
// 获取__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");
    }
    // Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 
    // 也就是说执行后 subClass.prototype.__proto__ === superClass.prototype; 这条语句为true
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            writable: true,
            configurable: true
        }
    });
    if (superClass) _setPrototypeOf(subClass, superClass);
}
// 设置__proto__
function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
    };
    return _setPrototypeOf(o, p);
}
// instanceof操作符包含对Symbol的处理
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");
    }
}
// 按照它们的属性描述符 把方法和静态属性赋值到构造函数的prototype和构造器函数上
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);
    }
}
// 把方法和静态属性赋值到构造函数的prototype和构造器函数上
function _createClass(Constructor, protoProps, 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
        // 所以也就是相当于Parent.call(this, name); 是super(name)的一种转换
        // _possibleConstructorReturn 判断Parent.call(this, name)函数返回值 是否为null或者函数或者对象。
        _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 18

Если вы еще мало знаете о наследовании JS, рекомендуется прочитать соответствующие главы следующих книг, и вы можете найти соответствующиеpdfВерсия.

Рекомендуем прочитать главы книги, связанные с наследованием JS

«Расширенное программирование на JavaScript, 3-е издание» — глава 6, объектно-ориентированное программирование, 6 схем наследования, а именно наследование цепочки прототипов, наследование заимствованного конструктора, наследование композиции, наследование прототипов, паразитное наследование и паразитное композиционное наследование.Адрес книги сообщества Тьюринга, опубликовано позжеgithubСсылки, которые содержат эти унаследованные кодыdemo.

«Объектно-ориентированное программирование JavaScript, 2-е издание» — Глава 6 Наследование, 12 видов схем наследования. 1. Метод цепочки прототипов (традиция имитации), 2. Только наследование прототипа, 3. Метод временного конструктора, 4. Метод копирования атрибутов прототипа, 5. Метод полного копирования атрибутов (т.е. метод поверхностного копирования), 6. Метод глубокого копирования, 7. Наследование прототипа, 8. Режим расширения и улучшения, 9. Множественное наследование, 10. Паразитическое наследование, 11. Заимствование конструктора, 12. Заимствование конструктора и копирование атрибутов.

Введение в стандарт ES6 — Глава 21. Наследование классов

«Глубокое пониманиеES6- Глава 9 JavaScriptкласс в

"Что ты не знаешьJavaScript-Том 1, Глава 6 Поведенческое делегирование и Приложение AES6中的class

Суммировать

Наследование для JS означает методы, свойства, статические методы и т. д., принадлежащие родительскому классу, и дочерний класс также должен иметь это. В подклассе для поиска можно использовать цепочку прототипов, также можно вызвать родительский класс в подклассе или скопировать копию из родительского класса в подкласс. Методов наследования может быть много, ключ в том, чтобы понимать и быть знакомым с ними. Зная, как работают эти объекты, прототипы и конструкторы, остальное несложно.Паразитическая композиционная наследственностьЕго больше используют разработчики. Рассмотрите паразитарное композиционное наследование. Есть три основных момента:

    1. конструктор подкласса__proto__Укажите на конструктор родительского класса, наследуйте статический метод родительского класса
    1. конструктор подклассаprototypeиз__proto__в конструктор родительского классаprototype, наследует метод родительского класса.
    1. Конструктор родительского класса вызывается в конструкторе дочернего класса для наследования свойств родительского класса.

На этом статья в основном закончена. Ресурсы, такие как код статьи и изображения, размещаются здесьgithub inhertа такжеdemoэкспонатes6-extends, в сочетании сconsole、sourceПанельный обзор лучше.

Читатели, которые находят что-то неправильным или могут быть улучшены, могут оставить комментарий. Кроме того, если вы считаете, что написано хорошо, вы можете поставить лайк, прокомментировать и переслать, что также является своего рода поддержкой для автора.

Избранные статьи автора

Изучите общую архитектуру исходного кода sentry и создайте собственный SDK для мониторинга исключений переднего плана.
Изучите общую архитектуру исходного кода lodash и создайте собственную библиотеку классов функционального программирования.
Изучите общую архитектуру исходного кода подчеркивания и создайте собственную библиотеку классов функционального программирования.
Изучите общую архитектуру исходного кода jQuery и создайте собственную библиотеку js.
Интервьюер спросил: наследование JS
Интервьюер спросил: этот пункт JS
Интервьюер спросил: Могу ли я смоделировать вызов и применить методы JS?
Интервьюер спросил: Могу ли я смоделировать метод привязки, реализующий JS?
Интервьюер спросил: Могу ли я смоделировать новый оператор, реализующий JS?
Внешний интерфейс использует сканер puppeteer для создания PDF-файла «React.js Book» и его слияния.

о

Автор: Чанг ИВакагаваНазвание смешано в реках и озерах. По дороге на фронт | Энтузиасты РРТ | Знаю очень мало, только хорошо учусь.
личный блог
segmentfaultСтолбец переднего видения, открылПередний планКолонка, добро пожаловать на внимание ~
Колонка самородков, добро пожаловать, обратите внимание~
Знайте переднюю колонку видения, открылПередний планКолонка, добро пожаловать на внимание ~
github blog, спроситьstar^_^~

Публичный аккаунт WeChat Ruochuan Vision

Может быть более интересным общедоступный аккаунт WeChat, нажмите и удерживайте, чтобы отсканировать код, чтобы следовать. Вы также можете добавить WeChatruochuan12, укажите источник и втяните вас в [Front-end Vision Exchange Group].

若川视野