JavaScript:The Good Parts読書ノート(三)


三.継承
  • 概要   Javascriptは弱いタイプの言語で、対象の起源はどうでもいいです.オブジェクトにとって重要なのは、どこから来たかではなく、何ができるかです.Javascriptは豊富なコード再利用モードを提供しています.これらはクラスベースのパターンをシミュレートできます.他のより表現力のあるモデルもサポートできます.JSで可能な継承パターンがたくさんあります.ここでは、いくつかの最も直接的なモデルを研究します.   クラスベースの言語では、オブジェクトはクラスの例であり、クラスは別のクラスから継承されてもよい.Javascriptはプロトタイプに基づく言語であり、これは対象が直接に他のオブジェクトから継承されることを意味する.
  • ダミーモード   Javascriptの原型には多くの矛盾が存在しています.オブジェクトを直接他のオブジェクトから継承せずに、余計な間接層を挿入します.このようなコンストラクタ関数は、疑似クラスです.関数オブジェクトが作成されると、Functionコンストラクタによって生成された関数オブジェクトは、以下のようなコードを実行します.    新しい関数のオブジェクトは、もう一つのconstructor属性を含むプロトタイプオブジェクトを与えられます.このconstructor属性の値はこの新しい関数自体です.Javascript言語はどの関数をコンストラクタとして決定する方法を提供していないからです.したがって、各関数はプロトタイプのオブジェクトを取得します.ほとんどの場合、constructor属性はあまり役に立たないので、重要なのはprototypeオブジェクトです.   ここでnew動作を一つの方法とすると、newを使用してオブジェクトを構成する場合、可能な実行コードは以下の通りである. 上から分かりました.コンストラクタ関数を定義し、そのプロトタイプを拡張します.    擬似モードはOO言語の継承をシミュレーションし、対象に接近することを意図しているが、それはどうもしっくりこないように見える.以下では、いくつかの補助的な方法によって、いくつかの醜い詳細を隠すことができる.    いくつかの煩雑なプロトタイプに対する操作の詳細を隠すことによって、疑似種を使って継承するのはそんなにおかしくないように見えますが、私達は本当に改善していますか?私たちは現在「クラス」のような挙動をしているコンストラクタ関数がありますが、よく見てみると、彼らは驚くべき行動があります.プライベート環境がなく、すべての属性が公開されています.父の種類にアクセスできませんでした.最悪なのは、コンストラクタを使って深刻な被害があります.もしあなたがコンストラクタ機能を呼び出した時に、前にnewオペレータを追加することを忘れたら、thisはグローバルオブジェクトに結び付けられます.これにより、新しいオブジェクトを拡張することもなく、グローバル変数を破壊します.また、コンパイル時の警告もないし、実行時の警告もありません.これは深刻な言語設計ミスです.この問題が発生するリスクを低減するために、すべてのコンストラクタ関数はイニシャルで大文字で命名することを約束しています.   「疑似クラス」形式はJavascriptに慣れていないプログラマに便利を提供することができますが、その言語の本当の本質を隠しています.参考類の表示法はプログラマーを誤って書きすぎて複雑な階層構造になるかもしれない.Javascriptの中でもっと良い選択があります.
  • 関数化モード    純粋なモデルでは、クラスを捨てて、対象に集中します.プロトタイプの継承はクラスベースの継承よりも概念的に簡単です.新しいオブジェクトは古いオブジェクトの属性を継承できます.有用な基礎オブジェクトを作成することによって、そのオブジェクトと似たようなオブジェクトをより多く構築することができます.一つのアプリケーションをいくつかの列の入れ子の抽象類に分解する分類過程を完全に避けることができます.
    this.prototype = {constructor : this};
     これは一種の「差別化相続」です.この新しいオブジェクトをカスタマイズすることによって、その基本的なオブジェクトとの違いを指定しました.
  • プライバシーの保護    これまで述べてきた継承パターンには弱点があります.私たちはプライバシーを守ることができません.対象のすべての属性が見られます.私たちはプライベート変数とプライベート関数の保護を設定します.その中の一つの解決法はモジュールモードを使用することである.以下はモジュールモードを使用した疑似コードテンプレートです.
    Function.method("new", function(){
           //             
         var that = Object.beget(this.prototype);
           //       ,   this      
         var other = this.apply(that,arguments);
           //             ,      
         return (typeof other === 'object' && other) || that;
    }
     使用例:
    var Mammal = function(name){ //      ,           
    	this.name = name;
    };
    Mammal.prototype.get_name = function(){
    	return this.name;
    };
    Mammal.prototype.says = function(){
    	return this.saying || "";
    };
    
    var myMammal = new Mammal("Herb the Mammal");
    var name = myMammal.get_name();
    document.writeln(name);
    
    var Cat = function(name){
    	this.name = name;
    	this.saying = "meow";
    };
    
    //   Cat.prototype,  "  " Mannal
    Cat.prototype = new Mammal();
    Cat.prototype.purr = function(n){
    	var i,s = "";
    	for(i = 0 ; i < n ; i ++){
    		if(s){
    			s+="-";
    		}
    		s += "r";
    	}
    	return s;
    };
    //        get_name()
    Cat.prototype.get_name = function(){
    	return this.says() + " " + this.name + " " + this.says();
    };
    
    var myCat = new Cat("Henrietta");
    document.writeln(myCat.says()); // meow
    document.writeln(myCat.purr(5)); // r-r-r-r-r
    document.writeln(myCat.get_name()); // meow Henrietta meow
    
     
  • 高度な関数化モード    アーティファクトモードでは、アーキテクチャー関数Catは、その基質コンストラクタMammalがすでに完了した作業(自身の属性を初期化する)を繰り返さなければならない.また、関数化モードでは、もはやこのような作業が必要ではない.コンストラクタcatはコンストラクターmammalを呼び出し、mammalに初期化作業を完了させるためである.だからCatは自分の違いに注目すればいいです.
    Function.method("inherits",function(Parent){
    	this.prototype = new Parent();
    	return this;
    });
    
    var Dog = function(name){
    	this.name = name;
    	this.saying = "wanwan";
    }.inherits(Mammal).method("purr",function(n){
    	var i,s = "";
    	for(i = 0 ; i < n ; i ++){
    		if(s){
    			s+="-";
    		}
    		s += "w";
    	}
    	return s;
    }).method("get_name",function(){
    	return this.says() + " " + this.name +" " + this.says();
    });
    
    var myDog = new Dog("Kiten");
    document.writeln(myDog.says()); // wanwan
    document.writeln(myDog.purr(5)); // w-w-w-w-w
    document.writeln(myDog.get_name()); // wanwan Kiten wanwan
    
        関数化モードはまた、親方法を扱う機会を与えてくれます.superior法を構築し、方法名を取得し、どの方法の関数を呼び出しますか?関数は元の方法を呼び出します.(デコレーション/プロキシモード)
    //                    :
    var myMammal = {
    	name : "Herb the Mammal",
    	get_name : function(){
    		return this.name;
    	},
    	says : function(){
    		return this.saying || "";
    	}
    };
    
    //           beget           ,         。
    var myCat = Object.beget(myMammal);
    myCat.name = "Henrietta";
    myCat.saying = "meow";
    myCat.purr = function(n){
    	var i,s="";
    	for(i = 0 ; i < n ; i ++){
    		if(s){
    			s+="-";
    		}
    		s+="s";
    	}
    	return s;
    };
    
    myCat.get_name = function(){
    	return this.says + " " + this.name + " " + this.says;
    };
        関数化モードは大きな柔軟性があります.これは擬似モードのように多くの手間が必要なだけでなく、より良いパッケージと情報隠蔽を得て、また親の方法を訪問する能力が必要です.私たちは一つの部品からオブジェクトを組み合わせることができます.例えば、簡単なイベントの処理特性を任意のオブジェクトに追加できる関数を構築することができます.彼はオブジェクトに対して、一つのファイティング方法と一つのプライベートなイベントのレジストリオブジェクトを追加します.
    //   var constructor = function(spec,my){
    //      var that,         ;
    //      my = my || {};
    //                 my ;
    //      that =      ;
    //        that     (    );
    //      return that;
    //   }
    
        このようにして、1つのコンストラクタ関数は、コンポーネント(関数)のセットからオブジェクトをセット化することができます.Javascriptの弱いタイプはここで大きな利点があります.全タイプのシステムに関心を持つ必要がないからです.反対に、私たちは彼らの個性的な内容に集中できます.