Javascriptの中の構造関数

3651 ワード

工場モード
function createPerson(name, age, job) {
  var o = new Object();
  o.name = name;
  o.job = job;
  o.sayName = function () {
    alert(this.name);  
  };
  return o;
}
var person1 = createPerson("Mike", 29, "Dev");
var person2 = createPerson("Greg", 27, "Doctor");
工場モードでは、似たようなオブジェクトを複数作成する問題を解決しましたが、対象認識の問題は解決されませんでした.
コンストラクタモード
function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function () {
    alert(this.name);  
  };
}
var person1 = new Person("Mike", 29, "Dev");
var person2 = new Person("Greg", 27, "Doctor");
コンストラクタモードは工場モードと比較します.
  • は、オブジェクトを明示的に作成していません.
  • は直接に属性と方法をthisオブジェクトに与えた.
  • にはreturn文がありません.
  • 前の例の最後に、person1およびperson2はそれぞれPersonの異なる例を保存している.これらの2つのオブジェクトは、constructor(構造関数)属性を持っています.この属性はPersonを指しています.
    alert(person1.constructor === Person);      //true
    alert(person2.constructor === Person);      //true
    
    私たちが作成したオブジェクトはObjectの例であり、Personの例でもあります.
    alert(person1 instanceof Object);       //true
    alert(person1 instanceof Person);       //true
    
    カスタムコンストラクタを作成すると、将来はそのインスタンスを特定のタイプとして識別することができるということです.これは工場モデルよりも構造関数モードの方が優れているところです.この例では、instanceofが同時にperson1の例であるのは、すべてのオブジェクトがObjectから継承されているからである.
    コンストラクタを関数として使う
    コンストラクタと他の関数との唯一の違いは,それらを呼び出す方法が異なることにある.しかし、コンストラクタは結局関数であり、コンストラクタを定義する特殊な文法は存在しない.任意の関数は、Objectオペレータを介して呼び出されれば、構築関数として機能することができる.どの関数も、newオペレータを介して呼び出さなければ、普通の関数と同じではない.
    //         
    var person = new Person("Mike", 29, "Dev");
    person.sayName();      // "Mike"
    
    //         
    Person("Greg", 27, "Doctor");    //    window
    window.sayName();     // "Greg"
    
    //              
    var o = new Object();
    Person.call(0, "Kristen", 25, "Nurse");
    o.sayName();      // "Kristen"
    
    構造関数の問題
    コンストラクタを使用する主要な問題は、各方法が各インスタンスで再作成されます.前の例では、newおよびperson1はいずれもperson2という方法があるが、その2つの方法は同じsayName()の例ではない.ECMAScriptの関数はオブジェクトですので、関数を定義するごとにオブジェクトを実例化しました.論理的には、この時の構造関数もこのように定義されています.
    function Person(name, age, job) {
      this.name = name;
      this.age = age;
      this.job = job;
      this.sayName = new Function("alert(this.name)");
    }
    
    この観点からコンストラクタを見ると、各Function例は、異なるPerson例の本質を含むことが分かりやすい.このように構成関数を作成すると、異なる作用ドメインチェーンおよび識別子解析が生じるが、Functionの新しいインスタンスを作成するメカニズムは依然として同じである.したがって、異なる例における同名関数は、等ではない.以下のコードはこの点を証明できます.
    alert(person1.sayName === person2.sayName);     //false
    
    しかし、完全に同じタスクを2つ作成するFunctionの例は、必要ではない.さらに、Functionオブジェクトがあります.コードを実行する前に関数を特定のオブジェクトに結びつける必要はありません.従って、この問題は以下のように関数定義を構造関数の外部に移すことによって解決できる.
    function Person(name, age, job) {
      this.name = name;
      this.age = age;
      this.job = job;
      this.sayName = sayName;
    }
    
    function sayName() {
      alert(this.name);
    }
    
    この例では、this関数の定義を構造関数の外部に転送します.構造関数の内部では、sayName()属性を大域のsayName関数に等しく設定します.このように、sayNameは関数を指すポインタを含むので、sayNameおよびperson1オブジェクトは、グローバルスコープで定義された同じperson2関数を共有する.このようにして確かに二つの関数が同じことをする問題を解決しましたが、新しい問題がまた来ました.グローバルスコープで定義された関数は実際にはある対象にしか呼び出されません.さらに納得できないのは、対象がいろいろな方法を定義する必要があるなら、多くの大域関数を定義しなければならないので、私たちのカスタム引用のタイプはパッケージ性が全くないということです.幸いにも、これらの問題はプロトタイプによって解決できます.