JAvascriptフレームワーク設計などの工場

13260 ワード

クラスと継承はjavascriptの出現で、javascriptがすでに大規模な開発の敷居に達していることを説明し、以前はECMAScript 4で、クラス、モジュールなどを導入しようとしたが、あまりにも多くの特性を導入しすぎたため、javascriptは黒煙瘴気になり、否決された.クラスをES 6に遅延しただけだ.これまでjavascriptには本当の意味でのクラスはありませんでした.しかし、私たちはクラスをシミュレートすることができます.この間、クラス工場はフレームワークの標準です.本章では、さまざまなクラスの実現を紹介し、自分のフレームワークの中で、あるいは選択するときに自分が好きなスタイルを便利にします.
1.javascriptクラスのサポート
他の言語では、クラスのインスタンスは構造関数newによって出てきます.javaを意図的に真似する言語として.JAvascriptにはnewオペレータが存在し、すべての関数をコンストラクタとして使用できます.コンストラクション関数は通常の方法と何の違いもありません.ブラウザは、ノード、Element、HTML Element、HTML ParagraphElementなど、華やかな生態圏を構築するために、継承関係を使用していくつかの方法や属性の共有を容易にすることが明らかになったので、javascriptは他の言語からプロトタイプというメカニズムを参考にしました.Prototypeは、各関数に特殊なオブジェクト属性として存在します.1つの関数がnewオペレータnewを介して「子供」である「インスタンス」を出力すると、インスタンスというオブジェクトは、この関数のPrototypeオブジェクトのすべてのメンバーを有し、すべてのインスタンスオブジェクトがメソッドまたは属性のセットを共有することを実現します.javascriptのいわゆる「クラス」は、このPrototypeオブジェクトを修正することで、オリジナルオブジェクトと他の定義の「クラス」を区別します.ブラウザでは、nodeというクラスはObjectに基づいて修正され、ElementはNodeに基づいており、HTML ElementはElementに基づいている.デルのワークビジネスに対して、再利用と共有を実現するために独自のクラスを作成できます.

  function A(){

  }
  A.prototype = {
    aa:"aa",
    method:function(){
    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);
  console.log(a.method === b.method)


一般的に、プロトタイプに定義されたメソッドをプロトタイプメソッドと呼び、すべてのインスタンスに共有されます.これは良いことも悪いこともあります.差別化を実現するためにjavascriptでは、コンストラクタ内で直接メソッドを指定することができます.これを特権メソッドと言います.属性の場合は、特権属性と呼ばれます.インスタンスごとにコピーが1つずつあり、影響はありません.したがって,通常,データを操作するための共有方法をプロトタイプに置き,プライベート属性を特権属性に置く.しかし、thisに置くと、勝手にアクセスさせられるので、関数内の役割ドメイン内に置きましょう.このとき、名実ともにプライベート属性になります.

  function A() {
    var count = 0;
    this.aa = "aa";
    this.method = function() {
      return count;
    }
    this.obj = {}
  }
  A.prototype = {
    aa:"aa",
    method:function(){

    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);//true   aa       ,   
  console.log(a.obj === b.obj) //false     ,             ,      。
  console.log(a.method === b.method); //false


特権メソッドまたは属性は,プロトタイプのメソッドまたは属性を隠すだけであるため,特権メソッドを削除すれば,同名のプロトタイプメソッドまたは属性にメソッドを与えることができる.

  delete a.method;
  delete b.method;
  console.log(a.method === A.prototype.method);//true
  console.log(a.method === b.method); //true


Javaの言語では,プロトタイプメソッドと特権メソッドの両方が属性インスタンスメソッドであり,javaにはクラスメソッドとクラス属性というものがある.javascriptでシミュレーションするのも簡単で、関数に直接定義すればいいです.

  A.method2 = function(){} //   
  var c = new A;
  console.log(c.method2); //undefined

次に、継承の実装を見てみましょう.Prototypeに何があるか、その例には何があるか、この属性が後に追加されたか、Prototype全体が置き換えられたかにかかわらず.このprototypeオブジェクトを別のクラスのプロトタイプに置き換えると、そのクラスのすべてのプロトタイプメンバーが簡単に得られます.

  function A() {};
  A.prototype = {
    aaa : 1
  }
  function B() {};
  B.prototype = A.prototype;
  var b = new B;
  console.log(b.aaa); //=> 1;
  A.prototype.bb = 2;
  console.log(b.bb) //=> 2;


同じオブジェクトを参照しているため、Aクラスのプロトタイプを修正することは、Bクラスのプロトタイプを修正することにも等しいことを意味します.したがって、1つのオブジェクトを2つのクラスに割り当てることはできません.2つの方法があります
方法1:for inにより親クラスのプロトタイプメンバーを1つずつ子クラスに付与するプロトタイプ方法2は,子クラスのプロトタイプが直接親クラスから取得されるのではなく,まず親クラスのプロトタイプを1つの関数に付与し,その後,この関数のインスタンスを子クラスのプロトタイプとする.
方法1,我々は通常mixinのような方法を実現しなければならない,ある本はコピー継承と呼ばれ,利点は簡単で直接的であり,悪い点はinstanceof検証に合格できないことである.Prototype.jsのextendメソッドはこのことをするために使用されます.

  function extend (des, source) { //des = destination
    for (var property in source)
      des[property] = source[property];
    return des;
  }

方法二、原型に頭を使うことから、原型継承と呼ぶ.次はお手本です

  function A() {};
  A.prototype = {
    aa:function(){
      alert(1)
    }
  }
  function bridge() {

  };
  bridge.prototype = A.prototype;

  function B() {}
  B.prototype = new bridge();

  var a = new A;
  var b = new B;

  console.log(a == b) //false         
  console.log(A.prototype == B.prototype) //true            
  console.log(a.aa === b.aa); //           
  A.prototype.bb = function () {
    alert(2)
  }
  //true,       
  B.prototype.cc = function (){
    alert(3)
  }
  //false         new  
  console.log(a.cc === b.cc)
  //         javascript       instanceof
  console.log(b instanceof A) ;//true
  console.log(b instanceof B) ; //true


方法2はinstanceofによって検証することができて、es 5はこの方法を内蔵して原型の継承を実現して、それはObjectです.createは、2番目のパラメータを考慮しない場合、次のコードにほぼ等しい.

  Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
  }


上記の方法では、親クラスのプロトタイプをパラメータとして入力し、子クラスのプロトタイプを返す必要があります.
しかし、子類は親の遺産を相続するだけでなく、自分のものを持つべきであり、また、原型相続は子類に親のメンバーと特権メンバーを相続させなかった.これらはクラスメンバーのように手動で追加する必要があります.私たちは上のextendメソッドを通じて、特権メンバーはサブクラスコンストラクタでapplyで実現することができます.

  function inherit(init, Parent, proto){
    function Son(){
      Parent.apply(this, argument); //          
      init.apply(this, argument); //         
    }
  }
  //  Object.create      ,           
  Son.prototype = Object.create(Parent.prototype,{});
  Son.prototype.toString = Parent.prototype.toString; //  IEbug
  Son.prototype.valueOf = Parent.prototype.valueOf; //  IEbug
  Son.prototype.constructor = Son; //         ,   Object
  extend(Son, proto) ;//            
  return Son;


次に,一連の実験を行い,次の例の遡及メカニズムをテストする.オブジェクトのプロパティにアクセスすると、彼はまず特権メンバーを探して、同名があれば戻って、なければプロトタイプを探して、もうなくて、親のプロトタイプを探して...そのプロトタイプを一時的に修正して、その属性がそれになるのを見てみましょう.

  function A(){

  }
  A.prototype = {
    aa:1
  }
  var a = new A;
  console.log(a.aa) ; //=>1

  //           
  A.prototype = {
    aa:2
  }
  console.log(a.aa); //=>1

  //              constructor  ,      
  //              ,javascript               
  function B(){

  }
  B.prototype = {
    aa:3
  }
  a.constructor = B;
  console.log(a.aa) //1       


したがって、クラスのインスタンスは必ず別のチャネルを介して遡及され、ecma仕様を参照すると、各オブジェクトにはnewのコンストラクタが参照するPrototypeオブジェクトを保存する内部属性[[prototype]]があることがわかります.標準ブラウザとIE 11では、__という名前が露出しています.proto__に表示されます.だから、動かない限りproto__上のコードはどのように動いて、a.aaは終始断固として1.
もう一度見て、new時に操作が何があったか見てみましょう.
1.空のオブジェクトを作成するinstance 2.instance.__proto__ = intanceClass.prototype 3.コンストラクション関数のthis=instance 4.コンストラクション関数のコード5を実行します.戻り値があるかどうかを判定し、戻り値がない場合はデフォルト値undefinedを返し、戻り値が複合データ型の場合はそのまま返し、そうでない場合はthisを返して次の結果になります.

  function A(){
    console.log(this.__proto__.aa); //1
    this.aa = 2
  }
  A.prototype = {aa:1}
  var a = new A;
  console.log(a.aa)
  a.__proto__ = {
    aa:3
  }
  console.log(a.aa) //=>2
  delete a. aa; //      ,           
  console.log(a.aa) //=>3


ありました_proto__,原型設計の継承をより簡単に設計することができますが、上記の例を直して、試験を行います.

  function A() {}
  A.prototype = {
    aa:1
  }
  function bridge() {}
  bridge.prototype = A.prototype;

  function B(){}
  B.prototype = new bridge();
  B.prototype.constructor = B;
  var b = new B;
  B.prototype.cc = function(){
    alert(3)
  }
  //String.prototype === new String().__proto__ => true
  console.log(B.prototype.__proto__ === A.prototype) //true
  console.log(b.__proto__ == B.prototype); //true 
  console.log(b.__proto__.__proto__ === A.prototype); //true          


なぜならb._proto__.constructorはBで、Bの原型はbridgeから得られたが、bride.prototype=A.prototype、逆に定義時、B.prototype._proto__ = A.prototypeは、2つのクラスの継承を簡単に実現できる.
__proto__プロパティはes 6に追加されているので、大胆な使用を防ぐことができます.
2.各種類工場の実現.
前節では,様々な継承方式の実装を実証したが,いずれも乱れている.ユーザーが対応するパラメータを入力したり、簡単なフォーマットでクラスを作成したりするだけで、専門的な方法を提供したいと考えています.特にサブクラス.
主流のフレームワークのクラス工場は彼らの複雑なツール関数に依存しすぎるため、精巧なクラス工場も百行程度にすぎない.
かなり精巧なライブラリ、P.js
https://github.com/jiayi2/pjs
使用:https://github.com/jiayi2/factoryjs
これはかなり精巧なライブラリで、特に親の同名メソッドを呼び出すと、親のプロトタイプを直接あなたの前に投げます.superも節約しました.

  var P = (function(prototype, ownProperty, undefined) {
 return function P(_superclass /* = Object */, definition) {
  // handle the case where no superclass is given
  if (definition === undefined) {
   definition = _superclass;
   _superclass = Object;
  }

  // C is the class to be returned.
  //
  // When called, creates and initializes an instance of C, unless
  // `this` is already an instance of C, then just initializes `this`;
  // either way, returns the instance of C that was initialized.
  //
  // TODO: the Chrome inspector shows all created objects as `C`
  //    rather than `Object`. Setting the .name property seems to
  //    have no effect. Is there a way to override this behavior?
  function C() {
   var self = this instanceof C ? this : new Bare;
   self.init.apply(self, arguments);
   return self;
  }

  // C.Bare is a class with a noop constructor. Its prototype will be
  // the same as C, so that instances of C.Bare are instances of C.
  // `new MyClass.Bare` then creates new instances of C without
  // calling .init().
  function Bare() {}
  C.Bare = Bare;

  // Extend the prototype chain: first use Bare to create an
  // uninitialized instance of the superclass, then set up Bare
  // to create instances of this class.
  var _super = Bare[prototype] = _superclass[prototype];
  var proto = Bare[prototype] = C[prototype] = C.p = new Bare;

  // pre-declaring the iteration variable for the loop below to save
  // a `var` keyword after minification
  var key;

  // set the constructor property on the prototype, for convenience
  proto.constructor = C;

  C.extend = function(def) { return P(C, def); }

  return (C.open = function(def) {
   if (typeof def === 'function') {
    // call the defining function with all the arguments you need
    // extensions captures the return value.
    def = def.call(C, proto, _super, C, _superclass);
   }

   // ...and extend it
   if (typeof def === 'object') {
    for (key in def) {
     if (ownProperty.call(def, key)) {
      proto[key] = def[key];
     }
    }
   }

   // if no init, assume we're inheriting from a non-Pjs class, so
   // default to using the superclass constructor.
   if (!('init' in proto)) proto.init = _superclass;

   return C;
  })(definition);
 }

 // as a minifier optimization, we've closured in a few helper functions
 // and the string 'prototype' (C[p] is much shorter than C.prototype)
})('prototype', ({}).hasOwnProperty);


クラスを作成しようとしました.

  var Dog = P (function(proto, superProto){
    proto.init = function(name) { //    
      this.name = name;
    }
    proto.move = function(meters){ //    
      console.log(this.name + " moved " + meters + " m.")
    }
  });
  var a = new Dog("aaa")
  var b = new Dog("bbb"); //     
  a.move(1);
  b.move(2);


現在の状況では、より簡潔な定義方法を作成してみましょう.

  var Animal = P (function(proto, superProto){
    proto.init = function(name) { //    
      this.name = name;
    }
    proto.move = function(meters){ //    
      console.log(this.name + " moved " + meters + " m.")
    }
  });
  var a = new Animal("aaa")
  var b = new Animal("bbb"); //     
  a.move(1);
  b.move(2);
  //...............
  var Snake = P (Animal, function(snake, animal){
    snake.init = function(name, eyes){
      animal.init.call(this, arguments); //       
      this.eyes = 2;
    }
    snake.move = function() {
      console.log('slithering...');
      animal.move.call(this, 5); //        
    }
  });
  var s = new Snake("snake", 1);
  s.move();
  console.log(s.name);
  console.log(s.eyes);


プライベートプロパティのデモは、関数内に配置されて定義されているため、安全で信頼性があります.

  var Cobra = P (Snake, function(cobra){
    var age = 1;//    
    //           
    cobra.glow = function(){ //  
      return age++;
    }
  });
  var c = new Cobra("cobra");
  console.log(c.glow()); //1
  console.log(c.glow()); //2
  console.log(c.glow()); //3

以上が本文のすべてですが、お好きになってください.