JavaScriptのプロトタイプとプロトタイプチェーン

6933 ワード

詳細については、githubにアクセスしてください.
クラスベースの言語では、オブジェクトはクラスのインスタンスであり、Javaなどの別のクラスからクラスを継承することができます.JavaScriptはプロトタイプベースの言語で、プロトタイプチェーンで継承を実現し、そのオブジェクトは直接別のオブジェクトから継承することができ、この編ではJavaScriptのプロトタイプとプロトタイプチェーンを詳しく述べる.
プロトタイプ
Javascriptで作成された各関数にはprototype属性があります.この属性はポインタであり、オブジェクトを指します.このオブジェクトの役割は、特定のタイプのインスタンスで共有できる属性と方法を含むことです.このオブジェクトは関数のプロトタイプオブジェクトです.
デフォルトでは、prototypeプロパティが存在する関数を指すポインタを含むconstructorプロパティがすべてのプロトタイプオブジェクトに存在します.
コンストラクション関数を呼び出して新しいインスタンスを作成すると、インスタンスの内部にコンストラクション関数のプロトタイプオブジェクトを指すポインタが含まれます.このポインタはECMA-262で[[Prototype]]と定義され、明示的にアクセスできませんが、Firefox、Safari、Chromeの各オブジェクトには__があります.proto__で行ないます.
__proto__インスタンスとコンストラクション関数の関係ではなく、インスタンスとコンストラクション関数のプロトタイプオブジェクトとの関係を示します.

    function Animal(name) {
        this.name = name;
    }
    Animal.prototype.age = 3;
    Animal.prototype.getName = function() {
        return this.name;
    };
    var animal = new Animal('Dog');
    console.log(animal.getName());  //  Dog

以上のコードのうち、Animalはコンストラクション関数、Animalである.prototypeは、コンストラクション関数のプロトタイプオブジェクトを指します.プロトタイプオブジェクトのconstructor属性は構造関数、すなわちAnimalを指す.prototype.constructorはAnimalを指す.コンストラクション関数の例では、_proto__プロパティは、コンストラクション関数のプロトタイプオブジェクトを指します.
  • プロトタイプとインスタンス属性アクセス
  •     
        function Animal(name) {
            this.name = name;
        }
        Animal.prototype.age = 3;
        Animal.prototype.getName = function() {
            return this.name;
        };
        var animal1 = new Animal('Dog');
        var animal2 = new Animal('Cat');
        console.log(animal1.age); //  3--    
        console.log(animal2.age); //  3--    
        animal2.age = 4;
        console.log(animal2.age); //  4--    
        delete animal2.age;
        console.log(animal2.age); //  3--    
    

    オブジェクトのプロパティを取得すると、まずオブジェクトインスタンス自体から開始し、インスタンスにプロパティが見つかった場合、そのプロパティ値を返します.見つからない場合は、インスタンスオブジェクトが指すコンストラクション関数プロトタイプオブジェクトの検索を続け、見つからない場合は値を返します.
    (1)hasOwnProperty()メソッドhasOwnProperty()メソッドは、オブジェクトインスタンスのプロパティが与えられたプロパティである場合にのみtrueを返すプロパティがプロトタイプ上にあるかインスタンス上にあるかを検出します.
    
        function Animal(name) {
            this.name = name;
        }
        Animal.prototype.age = 3;
        Animal.prototype.getName = function() {
            return this.name;
        };
        var animal1 = new Animal('Dog');
        var animal2 = new Animal('Cat');
        console.log(animal1.hasOwnProperty('name'));  //true
        console.log(animal1.hasOwnProperty('age'));  //false
        animal1.age = 4;
        console.log(animal1.hasOwnProperty('age'));  //true
    

    (2)プロトタイプがinオペレータをinと単独で使用する場合、オブジェクトを介して指定された属性にアクセスできる限りtrueを返します.属性がインスタンスで定義されているかプロトタイプで定義されているかにかかわらず、
    
        function Animal(name) {
            this.name = name;
        }
        Animal.prototype.age = 3;
        Animal.prototype.getName = function() {
            return this.name;
        };
        var animal1 = new Animal('Dog');
        var animal2 = new Animal('Cat');
        console.log(animal1.hasOwnProperty('name'));  //true
        console.log('name' in animal1); //true
        console.log(animal1.hasOwnProperty('age'));  //false
        console.log('age' in animal1); //true
        console.log('eat' in animal1); //false
    
  • 2.インスタンスとプロトタイプの参照関係
  • インスタンスが作成されると、その内部ポインタ[[prototype]](前述の__proto_)コンストラクション関数のプロトタイプオブジェクトを指し、参照関係があります.
    
        function Animal(name) {
            this.name = name;
        }
        var animal = new Animal('Dog');
        Animal.prototype.age = 3;
        Animal.prototype.getName = function() {
            return this.name;
        };
        animal.getName(); //  Dog
    

    インスタンスは、プロトタイプのgetNameメソッドよりも早く作成されたが、インスタンスがプロトタイプオブジェクトを参照することによって、プロトタイプオブジェクトの変化がインスタンスによって自然にアクセスできるため、メソッドを呼び出すことができることがわかります.しかし、次のような場合には、
    
        function Animal(name) {
            this.name = name;
        }
        var animal = new Animal('Dog');
        Animal.prototype = {
            constructor: Animal,  //  constructor  Animal,  constructor       ,       constructor    
            age: 3,
            getName: function() {
                return this.name;
            }
        };
        var animal2 = new Animal('Cat');
        console.log(animal2.getName());  //  Cat
        animal.getName();  //TypeError: undefined is not a function
    

    ここでは、まずインスタンスを作成し、次にコンストラクション関数のプロトタイプオブジェクトを書き換え、インスタンスでgetNameメソッドを呼び出すとエラーが発生します.コンストラクション関数プロトタイプオブジェクトを書き換えた後に作成されたインスタンス呼び出しgetNameメソッドは、通常、対応する値を返します.これは、プロトタイプオブジェクトを書き換えた後も、以前に作成したインスタンスが以前のプロトタイプオブジェクトを参照し、既存のプロトタイプとは関連がなく、その後に作成したインスタンス[[Prototype]]ポインタが既存のプロトタイプを参照するためです.
  • 3.プロトタイプオブジェクトに問題がある
  • 
        function Animal(name) {
            this.name = name;
        }
        Animal.prototype.age = 3;
        Animal.prototype.partner = ['one'];
        Animal.prototype.getName = function() {
            return this.name;
        };
        var animal1 = new Animal('Dog');
        var animal2 = new Animal('Cat');
        console.log(animal1.partner); //  ["one"] 
        console.log(animal2.partner); //  ["one"] 
        animal1.partner.push('two');
        console.log(animal1.partner); //  ["one", "two"] 
        console.log(animal2.partner); //  ["one", "two"] 
        console.log(animal1.partner === animal2.partner); //  true
    

    プロトタイプ内のすべてのプロパティがインスタンスで共有されていることがわかります.特に、参照タイプのプロパティでは、上記のパートナプロパティが表示されます.
  • 4.デフォルトプロトタイプ
  • すべての参照タイプのデフォルトはObjectから継承され、すべてのコンストラクタのデフォルトプロトタイプはObjectのインスタンスであり、デフォルトプロトタイプには内部ポインタが含まれ、Objectを指す.prototype.
  • 5.コンストラクション関数、プロトタイプとインスタンスの関係
  • 各コンストラクション関数にはprototypeプロパティによって指向されるプロトタイプオブジェクトがあります.プロトタイプオブジェクトには、コンストラクション関数を指すポインタconstructorが含まれます.インスタンスには、コンストラクション関数のプロトタイプオブジェクトを指す内部ポインタ[[prototype]]が含まれています.
    プロトタイプチェーン
    各コンストラクション関数にはプロトタイプオブジェクトがあり、あるプロトタイプオブジェクトを別のコンストラクション関数のインスタンスに等しくすると、そのプロトタイプオブジェクトには、このコンストラクション関数を指すプロトタイプオブジェクトを指すポインタが含まれます.このポインタが指すプロトタイプオブジェクトには、このコンストラクション関数を指すポインタが含まれます.同様に、ポインタが指すプロトタイプオブジェクトを別の構造関数のインスタンスに等しくすることができ、このように進むと、インスタンスとプロトタイプのチェーン、すなわちプロトタイプチェーンが形成される.
    
        function Parent() {
            this.name = 'parent';
        };
        Parent.prototype.getName = function() {
            return this.name;
        };
        function Child() {
            this.childname = 'child';
        }
        Child.prototype = new Parent();
        Child.prototype.getChildName = function() {
            return this.childname;
        };
        var child = new Child();
        console.log(child.getName()); //  parent
        console.log(child.getChildName());  //  child
    

    上記のコードのように、childインスタンスはChildプロトタイプを指し、ChildプロトタイプはParentインスタンス、すなわちParentプロトタイプを指す.可視本質は、新しいタイプ(コンストラクション関数)のインスタンスでプロトタイプオブジェクトを書き換え、プロトタイプチェーンを形成することである.
  • プロトタイプチェーン問題
  • プロトタイプオブジェクトに問題がある以上、プロトタイプチェーンも自然にこの問題を継承します.すなわち、プロトタイプ属性はすべてのインスタンスで共有され、プロトタイプ属性の変更はすべてのインスタンスに影響しますが、プロトタイプチェーンでは、あるプロトタイプオブジェクトが別の構造関数のインスタンスに等しいため、インスタンスが影響を受け、他のプロトタイプオブジェクトも影響を受けます.
    プロトタイプとプロトタイプチェーンはJavaScriptの継承を実現する基礎であり,次の記事ではJavaScriptの継承について詳しく説明する.