9、類とモジュール――「javascript権威の手引き」はノートを読みます.

4044 ワード

クラスとプロトタイプ
javascriptでは、クラスのすべてのインスタンスオブジェクトが同じプロトタイプオブジェクトから属性を継承します.そのため、原型の対象はクラスの核心です.以下の例ではinheit()関数が定義されていますが、この関数は新しく作成されたオブジェクトを返します.後者はある原型オブジェクトから継承されます.プロトタイプのオブジェクトを定義し、inheit()関数によって継承対象を作成すると、javascriptクラスが定義されます.一般的に、クラスのインスタンスはさらに初期化が必要であり、通常は、関数を定義して新しいオブジェクトを作成し、初期化することによって、この新しいオブジェクト==を作成する.以下の例では、「値の範囲」のクラスにプロトタイプオブジェクトを定義することを示し、「工場」関数を作成し初期化するための例も定義した.
例9-1:簡単なjavascript類
//            “range  ”
function range(from, to){
    //  inherit()      ,“                 ”
    //“               ”,     “    ”      (  )
    var r = inherit(range.methods);
    //    “    ”          
    //           ,            
    r.from = from;
    r.to = to;
    //          
    return r;
}

//        ,       range     
range.methods = {
    //  x    ,   true,    false
    //            ,             
    includes: function(x){
        return this.from <= x && x <= this.to;
    },
    //               f
    //            
    foreach: function(f){
        for(var x = Math.ceil(this.from); x <= this.to; x++){
            f(x);
        }
    },
    //            
    toString: function(){
        return "(" + this.from + "..." + this.to + ")";
    }
};

//     “range  ”     
var r = range(1, 3);
r.includes(2);              //true
r.foreach(console.log);     //  1、2、3
console.log(r.toString());  //  (1...3)

function inherit(p){
    if(p == null) 
        throw TypeError();              // p      ,    null( undefined)
    if(Object.create)                   //  Object.create()  
        return Object.create(p);        //     
    var t = typeof p;                   //         
    if(t !== "object" && t !== "function")
        throw TypeError();
    function f(){};                     //         
    f.prototype = p;                    //         p
    return new f();                     //  f()  p     
}
クラスとコンストラクタ
例9−1は、javascriptにおいてクラスを定義する方法の一つを示している.しかし,この方法は一般的ではないので,構造関数を定義していないので,新たに作成したオブジェクトを初期化するために構成関数が使われている.キーワードnewを使って構造関数を呼び出します.newを使ってコンストラクタを呼び出すと自動的に新しいオブジェクトが作成されますので、コンストラクタ自体はこの新しいオブジェクトの状態を初期化するだけでいいです.コンストラクタを呼び出す重要な特徴は,新しいオブジェクトのプロトタイプとしてコンストラクタのプロファイルを使用することである.これは同じ構造関数を通して作成されたすべてのオブジェクトが同じオブジェクトから継承されていることを意味します.したがって、彼らは同じクラスのオブジェクトです.例9-2は例9-1の「範囲類」を修正し、工場関数の代わりにコンストラクタを使用した:
下記の短いコードを使って上の重点部分を説明します.
var o = {x: "aaaa"};
function f(){}
f.prototype = o;
var result = new f();
//              ,
//     prototype  (f.prototype)         (result.__proto__)
console.log(result.__proto__ === f.prototype);
例9-2:構造関数を使用して「範囲類」を定義する
//        ,         “    ”
//  ,              ,      
function Range(from, to){
    //    “    ”          
    //           ,            
    this.from = from;
    this.to = to;
}

//   “    ”        
//  ,        “prototype”
Range.prototype = {
    //  x    ,   true,    false
    //            ,             
    includes: function(x){
        return this.from <= x && x <= this.to;
    },
    //               f
    //            
    foreach: function(f){
        for(var x = Math.ceil(this.from); x <= this.to; x++){
            f(x);
        }
    },
    //            
    toString: function(){
        return "(" + this.from + "..." + this.to + ")";
    }
};

//     “range  ”     
var r = new Range(1, 3);
r.includes(2);              //true
r.foreach(console.log);     //  1、2、3
console.log(r.toString());  //  (1...3)
この二つの例コードの違いが見られます.
  • 9-1例における工場法レンゲ()はRange()に変換される(ここでは一般的な約束に従う:構造関数を定義することは定義クラスであり、クラス名の頭文字は大文字にする)
  • Range()構造関数はnewキーワードで呼び出されるが、range()工場関数はnewを使用する必要はない.
  • コンストラクタとクラスの標識
    constructorのプロパティ
    アヒル形弁形
    上記で説明した検出対象の種類の様々な技術は、少なくともクライアントjavascriptでは問題がある.解決策は、これらの問題を回避することです.「対象の種類は何ですか?」ではなく、「対象が何ができるか」に注目してください.このような考え方はPythonやRubyで非常に一般的で、「アヒルの弁別型」と呼ばれています.
    アヒルのように歩いたり泳いだりします.
    プログラム言語で言えば、「もし一つの対象がアヒルのように歩いたり、泳いだりして、ガツガツと呼ばれたら、この対象はアヒルであっても、たとえそれがアヒル類の原型の対象から継承されたものではないとしても」ということです.