基礎学習-JSでの継承

8933 ワード

の原因となる
最近node-jsonwebtokenを使って次のコードを見つけました.とてもきれいだと思います.そこで、関連コードを探してみました.
コードアドレス、ここをクリック
var JsonWebTokenError = function (message, error) {
  Error.call(this, message);
  if(Error.captureStackTrace) {
    Error.captureStackTrace(this, this.constructor);
  }
  this.name = 'JsonWebTokenError';
  this.message = message;
  if (error) this.inner = error;
};

JsonWebTokenError.prototype = Object.create(Error.prototype);
JsonWebTokenError.prototype.constructor = JsonWebTokenError;

module.exports = JsonWebTokenError;

後でこのセグメントコードを分析します.
MDNの継承に関するセクションを見つけます(継承はプロトタイプチェーンでは高度なチュートリアルです).
JavaScriptでの継承は によって実現される.JSでの継承についてお話しします
継承とは?
継承はオブジェクト向けのソフトウェアの一つの概念である.オブジェクトに向かう2つの特徴は,それぞれ多態,分装である.継承は,自雷に親の属性やメソッドを持たせるか,再定義,追加属性やメソッドなどである.
wikipedia)
継承メソッド
プロトタイプチェーン継承
//   
       function Parent(name, age) {
           this.name = name,
           this.age = age,
           this.play = [1, 2, 3]
           this.setName = function () { }
       }
       Parent.prototype.setAge = function () { }
       //   
       function Child(price) {
           this.price = price
           this.setScore = function () { }
       }
       Child.prototype = new Parent() //                  
       var s1 = new Child(15000)
       var s2 = new Child(14000)
       console.log(s1,s2)

この継承方式はChild.prototypeParentオブジェクトに直接設定し,サブクラスはプロトタイプチェーンを介して親のすべての属性と方法にアクセスできるようになった.その後、新しい属性を追加するには、Child.prototype = new Parent()の後にある必要があります.そうしないと、上書きされます.
上記の例で説明する必要があることは、親属性を使用する場合はJSのデータ型変化ルールに従い、元の値(Primitive values)は変更不可(not mutation)、オブジェクトは変更されます.このJSストレージタイプが決定します.元の値は毎回新しい値を返しますが、オブジェクトはメモリ内の領域アドレスを表し、アドレスは変わりません.具体的には、内部データが異なる場合でも、同じです.これはJSにおける深浅コピー問題を設計し,その後に触れる.
サブクラスコンストラクション関数を使用して継承
function Parent(name, age) {
    this.name = name,
    this.age = age,
    this.setName = function () {}
  }
  Parent.prototype.setAge = function () {}
  function Child(name, age, price) {
    Parent(this, name, age)  //    : this.Parent(name, age)
    /*this.name = name
    this.age = age*/
    this.price = price
  }
  var s1 = new Child('Tom', 20, 15000)

この方法は、まず、Childを初期化する前に、Childを初期化します.ここでは、newに関する知識点に関し、newの前にthisChild.prototypeというオブジェクトに初期化します.ここでは、Function.prototype.callを使用してParentを呼び出します.実際には、次のようなコードです.

const ChildPrototype = {
    Parent: function(name, age) {
        //...
    }
}

ここではParentでthisを用いて初期化された属性と方法が得られ,ここではParentプロトタイプチェーンのデータは取得できない.この方法を使用すると、次のようなメリットとデメリットがあります.
メリット
  • プロトタイプチェーン継承、サブクラスが親参照属性にアクセスする問題
  • を解決した.
  • 子クラス初期化時に親クラスに
  • を渡すことができる.
  • マルチ継承(call複数の親オブジェクト)
  • を実現する.
    欠点
  • サブクラスインスタンスinstanceofを使用することは、親インスタンス
  • に等しくない.
  • 親プロトタイプチェーンを継承できない方法
  • 初期化のたびに親関数の初期化が必要であり、パフォーマンスが悪い
  • .
    ここで使用Function.prototype.callはこちらをご覧いただけます
    組合せ継承(プロトタイプチェーン+サブクラス構築関数)-1
    function Parent(name, age) {
        this.name = name,
        this.age = age,
        this.setAge = function () { }
    }
    Parent.prototype.setAge = function () {
        console.log("111")
    }
    function Child(name, age, price) {
        Parent.call(this, name, age)
        this.price = price
        this.setScore = function () { }
    }
    Child.prototype = new Parent()
    Child.prototype.contructor = Child
    Child.prototype.sayHello = function () { }
    var s1 = new Child('Tom', 20, 15000)
    console.log(s1)
    

    ここではプロトタイプチェーン継承と構造関数の利点を融合させた.しかし、欠点は、サブクラスを作成するときに2回の親クラスを呼び出すコンストラクション関数が呼び出されることです.上記のChild.prototype.contructor = Childでは、プロトタイプチェーンによる継承修復が必要である.ここでは主に前のいくつかの継承問題を修復します.
  • 親参照共有
  • 子クラス親クラスのすべての属性とメソッド
  • を取得できます.
    グループ継承-2
    function Parent(name, age) {
            this.name = name,
            this.age = age,
            this.setAge = function () { }
    }
    Parent.prototype.setAge = function () {
        console.log("111")
    }
    function Child(name, age, price) {
        Parent.call(this, name, age)
        this.price = price
        this.setScore = function () { }
    }
    Child.prototype = Parent.prototype
    Child.prototype.sayHello = function () { }
    var s1 = new Child('Tom', 20, 15000)
    console.log(s1)

    ここでは、子のprototypeParent.prototypeに再割り当てすることによって、親のすべてのプロトタイプチェーン上の属性、例えばsetAgeを継承することができ、子の関数宣言では、Parent.callを通じて親自身の属性と方法を継承することができる.このようにして、2回の呼び出し構造関数の問題を解決することができるが、ここでは、サブクラス構造関数Child.prototype.constructorが親構造関数によって覆われ、Parent.prototype.construtorおよびChild.prototype.constructorがいずれもParent.prototype.constructorを指すという問題もある.
    グループ継承-3
    function Parent(name, age) {
            this.name = name,
            this.age = age,
            this.setAge = function () { }
    }
    Parent.prototype.setAge = function () {
        console.log("111")
    }
    function Child(name, age, price) {
        Parent.call(this, name, age)
        this.price = price
        this.setScore = function () { }
    }
    Child.prototype = Object.create(Parent.prototype)
    Child.prototype.construct = Child
    Child.prototype.sayHello = function () { }
    var s1 = new Child('Tom', 20, 15000)
    console.log(s1)

    ここで、上記との違いは、Object.create()を用いてParent.prototypeChild.prototype.__proto__に設定することであり、すなわち、現在の構成は以下の図になる.
    このような方式の継承を通じて、現在では最も完璧な方式であり、以前の多くの問題も解決されている.
    ES 6のextendsキーワード
    
    class Parent {
        //        
        constructor(name, age) {
            this.name = name
            this.age = age
        }
        //       
        showName() {
            console.log("       ")
            console.log(this.name, this.age);
        }
    }
    let p1 = new  Parent('kobe', 39)
    console.log(p1)
    //      
    class Child extends Parent {
        constructor(name, age, salary) {
            super(name, age)//  super         
            this.salary = salary
        }
        showName() {//         
            console.log("       ")
            console.log(this.name, this.age, this.salary);
        }
    }
    let s1 = new Child('wade', 38, 1000000000)
    console.log(s1)
    s1.showName()
    

    この方式は現在のes 6の方式で、欠点は互換性で、すべてのブラウザがes 6と完璧に互換性があるわけではありません.しかし、他の言語と一致した継承文法が明らかで、分かりやすいです.
    実際のJSはプロトタイプチェーンで継承されているので、ここでは最終的に次のコードにコンパイルされます.
    これはBabelコンパイルの結果であり,プロトタイプチェーンによって実現されるかを見ることができる.
    
    
    "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); }
    
    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); }
    
    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 Test = function Test() {
      _classCallCheck(this, Test);
    };
    
    var Test1 =
    /*#__PURE__*/
    function (_Test) {
      _inherits(Test1, _Test);
    
      function Test1() {
        _classCallCheck(this, Test1);
    
        return _possibleConstructorReturn(this, _getPrototypeOf(Test1).apply(this, arguments));
      }
    
      return Test1;
    }(Test);
    

    次の図を見てください.
    ここでコードとコンビネーション継承3は似ていますよ
    詳細:
    https://www.quora.com/What-is...
    https://segmentfault.com/a/11...