JavaScriptの対象と原型、プロトタイプチェーン

8348 ワード

原文のリンク:https://juejin.im/post/5c4e996ef265da611d670436
オブジェクト
JavaScriptでは、万物はすべてObjectに対象されています.
Objectは属性の集合であり、個々のプロトタイプ[prototype object]を有しています.このプロトタイプ[prototype object]は、Objectまたはnull値であっても良いです.
一つのObjectのprototypeは内部の[[prototype]]属性の参照である.
プロトとプロトタイプ
  • [[prototype]]__proto__であり、__proto__属性の値はその対応するプロトタイプオブジェクトである.各オブジェクトには__proto__属性があり、自分が継承したプロトタイプを識別する.
    typeof function(){}.__proto__ === 'function' // true
    typeof {}.__proto__ === 'object' // true
    
  • は関数だけがprototype属性です.関数を作成すると、JSは関数のprototype属性を自動的に作成します.その値はconstructor属性のオブジェクトで、この方法のプロトタイプオブジェクトを指します.この関数をコンストラクタとして呼び出すと、JSはコンストラクタの一例を作成してくれます.例はコンストラクタprototypeのすべての属性と方法を継承します.
  • 原型チェーン
    JSは、__proto__prototypeの協力によってプロトタイプチェーンとオブジェクトの継承を実現した.
    公式の注釈:A prototype chain is a finite chain of object s which is used to implemented inherst and shared properties.
    構造関数は、__proto__によって共有される属性および方法を格納し、prototypeが既存のオブジェクトを指すように設定してオブジェクトを継承することもできる.
    オブジェクトのprototypeは、自己構成関数のprototypeを指す.__proto__のプロトタイプチェーンは、これによって生成される.
    jsのオペレータprototypeは、obj.__proto__.__proto__...をプローブすることによって、objがinstanceofであるかどうかを検証する例である.
    クラスをサポートする言語では、obj.__proto__.__proto__... === Constructor.prototypeを通じて、A継承Bを実現することができます.Javascript言語では、類の概念はないが、原型チェーンによって継承されることができます.この実現方法は原型継承です.
    例えば、a 1オブジェクトは、自身Constructorを設定することにより、自身のプロトタイプオブジェクトをaオブジェクトに指向させることにより、プロトタイプ継承が実現される.
    var a = {
      x: 10,
      calc: function(z) {
        return this.x + this.y + z
      }
    }
    
    var a1 = {
      y: 10,
      __proto__: a
    }
    
    console.log(`calc ${a1.calc(10)}`) // calc 30
    
    上記の例では、Class A extends B {}を呼び出したとき、a 1のcall方法を探してみたが、a 1のプロトタイプaに続き、この方法を発見して呼び出すことができなかったと分析した.__proto__方法には、それぞれ2つのthis上の属性x,yおよび1つの参照zの3つのパラメータがある.オブジェクト属性の呼び出し方式なので、thisはa 1を指し、そのうちa 1はx属性がないので、x属性は原型上のxの値を使い、y属性はa 1の属性を使い、zは10を伝えます.
    二つの注意点:
  • js検索はいずれも自身から探し始めます.見つけられない時、原型チェーンに沿ってa 1の原型を探します.見つけたら、この方法を呼び出します.見つからない時は引き続き上に探してください.原型がnullである時に、calc()undefinedに戻ります.
  • thisの指向は、メソッドを呼び出したときに決定され、プロトタイプチェーン検索の影響を受けない.
  • 上記のオブジェクト設定a1.calc(10)または関数設定calc()に加えて、一般的な作成オブジェクト方式は、コンストラクタである.
    構造関数
    上記の例と同様に、a 1の原型は対象aを継承し、構造関数として次のように書き換えます.
    function A(y) {
      this.x = 10;
      this.y = y;
      this.calc = function(z) {
        console.log(`x ${this.x}, y ${y}, z ${z}`);
        return this.x + this.y + z
      };
    } 
    
    A.prototype.name = 'A     name';
    var a2 = new A(20)
    console.log(`name: ${a2.name},    : ${a2.calc(30)}`);
    // x 10, y 20, z 30
    // name: A     name,    : 60
    console.log(
      a2.constructor === A,          // true
      a2.__proto__ === A.prototype,  // true
      a2.name === a2.__proto__.name, // true
      a2.name === A.prototype.name,  // true
      A.__proto__ === A.prototype,   // false
      `A   : ${A.__proto__ === Function.prototype}`, // A   : true
      `A    : ${A.prototype.constructor === A}`,    // A    : true
    );
    
    関数Aを作成すると、JSは関数Aのa1.__proto__ = a属性を自動的に作成します.その値はfunction A(){}; A.prototype = AA属性のオブジェクトです.キーワードprototypeを通じて、オブジェクトa 2を作成しました.例a 2は構造関数constructorのすべての属性と方法を継承しました.
    回顧総括
    function Foo(name) {
      this.name = name;
    }
    var foo1 = new Foo('foo1');
    var foo2 = new Foo('foo2');
    
    var obj1 = new Object();
    var obj2 = new Object();
    
    console.log(foo1.__proto__ === Foo.prototype); // true
    console.log(Foo.prototype.constructor === Foo); // true
    
    console.log(obj1.__proto__ === Object.prototype); // true
    console.log(Foo.prototype.__proto__ === Object.prototype); // true
    console.log(Object.prototype.constructor === Object); // true
    console.log(Object.__proto__ === Function.prototype); // true
    
    console.log(Function.prototype.constructor === Function); // true
    console.log(Function.__proto__ === Function.prototype); // true
    
  • オブジェクトは、属性newがあり、オブジェクトの構造関数のプロトタイプオブジェクトを指す.
  • 方法は、属性prototypeに加えて、属性__proto____proto__がこの方法のプロトタイプオブジェクトを指す.