JavaScriptにおける原型対象の徹底的な理解(徹底的に説明していますので、注意深く読まなければなりません)

9814 ワード

一、原型は何ですか?
プロトタイプはJavascriptの継承の基礎であり、JavaScriptの継承はプロトタイプによる継承である.
1.1関数の原型オブジェクト
JavaScriptでは、関数Aを作成します.ブラウザはメモリにオブジェクトBを作成します.各関数はデフォルトで属性があります. プロトタイプ このオブジェクトを指した属性の値はこのオブジェクトです. ).このオブジェクトBは関数Aのプロトタイプです.関数のプロトタイプと略称します.このプロトタイプBはデフォルトで属性があります. トラック この関数Aを指します.つまり、constructor属性の値は関数Aです.
下のコードを見てください.

    
        /*
                  ,               prototype 。               
                  ,               ,prototype          。      
                  constructor        

              :          :constructor。     Object    ,      。
        */
        function Person () {

        }       
    
以下の図は、関数を宣言した後に起こることを示している.
1.2立体関数を使ってオブジェクトを作成する
一つの関数を構造関数として(理論的にはどの関数も構造関数として)newを使ってオブジェクトを作成すると、このオブジェクトはデフォルトの非可視属性が存在し、構造関数のプロトタイプオブジェクトを指します. この見えない属性は一般的に[prototype]で表しますが、この属性は直接アクセスできません.
下のコードを見てください.

    
        function Person () {

        }   
        /*
                        ,                   [[prototype]],       
                        。
        */
        var p1 = new Person();
    
次の図面を観察します.
説明:
  • 上記の図からは、p 1オブジェクトを作成するにはPersonコンストラクタが使用されていますが、オブジェクトが作成された後、このp 1オブジェクトはPersonコンストラクタとは関係なく、p 1オブジェクトの「プロトタイプ」属性はPerson構文関数のプロトタイプオブジェクトを指しています.
  • new Person()を使用して複数のオブジェクトを作成すると、複数のオブジェクトが同時にPersonアーキテクチャ関数のプロトタイプオブジェクトを指す.
  • 私たちはこのプロトタイプオブジェクトに属性と方法を手動で追加できます.p 1、p 2、p 3…これらのオブジェクトはこれらのプロトタイプに追加された属性と方法を共有します.
  • 私たちはp 1の属性nameにアクセスし、p 1のオブジェクトの中で見つけたら、そのまま戻ってきます.p 1のオブジェクトの中に見つけられなかったら、p 1のオブジェクトの「プロトタイプ」属性が指すプロトタイプのオブジェクトの中で検索します.もし見つけられたら戻ります.
  • p 1オブジェクトに属性nameを追加すると、p 1オブジェクトはプロトタイプの属性nameをブロックします.つまり、p 1ではプロトタイプの属性nameにアクセスできません.
  • p 1オブジェクトを通してプロトタイプの属性nameの値しか読み取れません.プロトタイプの属性nameの値を変更することはできません.p 1.name=「李四」.プロトタイプの値を修正したのではなく、p 1オブジェクトに属性nameを追加しました.
  • .
    下のコードを見てください.
    
        
            function Person () {        
            }
            //     Person.prototype          
            // Person               name     "  "
            Person.prototype.name = "  ";
            Person.prototype.age = 20;
    
            var p1 = new Person();
            /*
                  p1     name,   p1               name,  
                p1  [[prototype]]          name  ,             name
                  。
                  :        p1    name  ,       p1      。
            */
            alert(p1.name);  //   
    
            var p2 = new Person();
            alert(p2.name);  //              ,    。
    
            alert(p1.name === p2.name);  // true
    
            //            ,         p1          name,   p1       
            //      。
            p1.name = "  ";
            alert("p1:" + p1.name);
            //   p2   name  ,  p2              。    
            alert("p2:" + p2.name);  //     
        
    
    二、原型に関するいくつかの属性と方法
    2.1 prototype属性
    プロトタイプはコンストラクタに存在しています.彼はこのコンストラクタの原型オブジェクトを指しています.
    前の図面を参照してください.
    2.2 constructor属性
    constructor属性は原型の対象に存在し、彼は構造関数を指しました.
    下のコードを見てください.
    
        function Person () {
        }
        alert(Person.prototype.constructor === Person); // true
        var p1 = new Person();
        //  instanceof               。  
        //typeof             。         instanceof,       typeof     object。
        alert(p1 instanceof Person);    // true
    
    私たちは必要に応じてPerson.prototype属性に新しいオブジェクトを指定してPersonの原型の対象とします.
    しかし、このとき問題があります.新しいオブジェクトのconstructor属性はPersonコンストラクタを指しません.
    下のコードを見てください.
    
        function Person () {
    
        }
        //   Person          。      constructor      Person  
        Person.prototype = {
            name:"  ",
            age:20
        };
        var p1 = new Person();
        alert(p1.name);  //   
    
        alert(p1 instanceof Person); // true
        alert(Person.prototype.constructor === Person); //false
        //  constructor     ,    Person.prototype          :
        /*
        Person.prototype = {
            constructor : Person    // constructor    Person  
        }
        */
    
    2.3__proto__属性(注意:左右それぞれ2つの下線)
    新しいオブジェクトを作成すると、このオブジェクトにはデフォルトでアクセスできない属性があります.この属性は構造方法の原型オブジェクトを指します.
    ただし、個別ブラウザでは、この属性に対するアクセス(chromeブラウザと火狐ブラウザ.ieブラウザはサポートしていません)も提供されています.
    しかし、開発者は、このような方法でアクセスしないようにしてください.操作が不注意なため、このオブジェクトの継承チェーンが変更されます.
    
        function Person () {
    
        }
        //   Person          。      constructor      Person  
        Person.prototype = {
            constructor : Person,
            name:"  ",
            age:20
        };
        var p1 = new Person();
    
        alert(p1.__proto__ === Person.prototype);   //true
    
    
    2.4 hasOwnProperty()方法
    ご存知のように、私達が一つの対象の属性を訪問する時、この属性は対象自体から来る可能性もありますし、この対象の属性が指すプロトタイプから来る可能性もあります.
    この相手の出所はどう判断しますか?
    hasOwnProperty方法は、属性がオブジェクト自身から来るかどうかを判断することができます.
    
        function Person () {
    
        }
        Person.prototype.name = "  ";
        var p1 = new Person();
        p1.sex = " ";
        //sex      p1     ,   true
        alert("sex        :" + p1.hasOwnProperty("sex"));
        // name          ,   false
        alert("name        :" + p1.hasOwnProperty("name"));
        //  age      ,    false
        alert("age          :" + p1.hasOwnProperty("age"));
    
    
    したがって、オブジェクト自体にオブジェクトが追加されているかどうかを判断することができますが、原型に存在するかどうかは判断できません.この属性は存在しない可能性があります.
    つまり、プロトタイプの属性と存在しない属性はfasleに戻ります.
    どのように属性が原型に存在するかを判断しますか?
    2.5 in操作子
    in操作符は、属性がこのオブジェクトに存在するかどうかを判断します.ただし、この属性を検索するときは、現在のオブジェクト自体の中で探しています.対象が見つからない場合は、元のモデルに探してください.言い換えれば、オブジェクトと原型の中にこの属性が存在する場所があれば、trueに戻ります.
    
        function Person () {
    
        }
        Person.prototype.name = "  ";
        var p1 = new Person();
        p1.sex = " ";
        alert("sex" in p1);     //        ,  true
        alert("name" in p1);    //     ,  true
        alert("age" in p1);     //          ,  false
    
    
    前の問題に戻り、属性が原型に存在するかどうかを判断すると、
    属性が存在しますが、対象自体に存在しない場合は、必ず原型に存在します.
    
        function Person () {
        }
        Person.prototype.name = "  ";
        var p1 = new Person();
        p1.sex = " ";
    
        //                
        function propertyLocation(obj, prop){
            if(!(prop in obj)){
                alert(prop + "     ");
            }else if(obj.hasOwnProperty(prop)){
                alert(prop + "        ");
            }else {
                alert(prop + "        ");
            }
        }
        propertyLocation(p1, "age");
        propertyLocation(p1, "name");
        propertyLocation(p1, "sex");
    
    三、原型モデルと構造関数モデルを組み合わせてオブジェクトを作成する
    3.1原型モデル作成対象の欠陥
    プロトタイプの中のすべての属性は共有されています.つまり、同じコンストラクションで作成したオブジェクトでプロトタイプの属性にアクセスするときは、みんなが訪問した同じオブジェクトです.オブジェクトがプロトタイプの属性を変更したら、すべてのオブジェクトに反映されます.
    しかし、実際の使用では、オブジェクトごとに属性が異なります.張三の名前は張三、李四の名前は李四です.
    ​ ==しかし、この共有特性は方法(属性値は関数の属性)に対しても非常に適切である.==すべてのオブジェクト共有方法は最適な状態である.このような特性はcciとJavaにおいて生まれつき存在する.
    3.2構造関数モデルの作成対象の欠陥
    コンストラクタに加えられた属性と方法は、それぞれのオブジェクトに独自のものがあります.みんなは共有しません.この特性は属性に適していますが、方法にはあまり適していません.すべてのオブジェクトにとって、彼らの方法は一つで十分です.一人分を必要とせず、メモリの無駄と性能の低下をもたらします.
    
        function Person() {
            this.name = "  ";
            this.age = 20;
            this.eat = function() {
                alert("    ");
            }
        }
        var p1 = new Person();
        var p2 = new Person();
        //            
        alert(p1.eat === p2.eat); //fasle
    
    以下の方法で解決できます.
    
        function Person() {
            this.name = "  ";
            this.age = 20;
            this.eat = eat;
        }
        function eat() {
            alert("    ");
        }
        var p1 = new Person();
        var p2 = new Person();
        //  eat            ,   true
        alert(p1.eat === p2.eat); //true
    
    このような解決方法は致命的な欠陥があります.パッケージ性があまりにも悪いです.対象に向けて使用する目的の一つはパッケージコードです.この時は性能のためにまたコードを対象から抜き出すことができます.これは反人類の設計です.
    3.3組み合わせモードを使用して上記の2つの欠陥を解決する.
    プロトタイプモードはパッケージ方法に適しており、構造関数モードはパッケージ属性に適合しており、2つのモードを統合するという利点がある.
    
        //           
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }
        //          
        Person.prototype.eat = function (food) {
            alert(this.name + "  " + food);
        }
        Person.prototype.play = function (playName) {
            alert(this.name + "  " + playName);
        }
    
        var p1 = new Person("  ", 20);
        var p2 = new Person("  ", 30);
        p1.eat("  ");
        p2.eat("  ");
        p1.play("  ");
        p2.play("  ");
    
    四、動原型モード作成対象
    前に述べたコンビネーションモデルも完璧ではなく、完璧ではないという点もあります.構造方法と原型を分けて書くと、どうも気分が悪くなります.構造方法と原型を一緒に包装するように工夫しなければなりません.
    動的プロトタイプモードは,すべての属性と方法を構造法に実装し,必要なときだけプロトタイプを初期化し,コンストラクターとプロトタイプを同時に使用する利点を維持した.
    下のコードを見てください.
    
        //          
        function Person(name, age) {
            //            
            this.name = name;
            this.age = age;
            /*
                  this.eat       function,    function           ,
                    funcion      。
                   function,              ,       。
                perfect!               。
            */
            if(typeof this.eat !== "function"){
                Person.prototype.eat = function () {
                    alert(this.name + "   ");
                }
            }
        }
        var p1 = new Person("  ", 40);
        p1.eat();   
    
    説明:
  • 合成モードと動的プロトタイプモードは、JavaScriptにおいて比較的多くの2つのオブジェクトを作成する方式である.
  • は今後ダイナミックプロトタイプを使用することを提案しています.彼は組み合わせモードのパッケージが不完全な欠点を解決しました.
  • 回転:https://blog.csdn.net/u012468376/article/details/53121081
  • コア思想:各オブジェクトの属性は違っていますが、方法は同じでなければなりません.コンストラクタの中にはオブジェクトが同じではないが、同じ属性ではないので、最適な方法は動的プロトタイプのオブジェクトを作成し、この問題を解決しました.