Javascript:prototype漫談から継承まで(2)

9946 ワード

本文は同時に私のもう一つの独立ブログ『Javascript:prototype漫談から継承(2)』を発表しました.(管理人さん、注意してください.この二つは全部私のオリジナルブログです.トップページを蹴らないでください.転載ではありません.もう三回誤解しました.)
前の章漫談の継承の最後に、最初のより完璧な解決策を得た.
function extend(Child, Parent) {
  var F = function(){};
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  //         prototype,      prototype.constructor,
  //         
  Child.prototype.constructor = Child;
  //
  //          
  Child.uber = Parent.prototype;
}
 
この方法の「比較的完璧」なところは、中間変数var F = function(){}を導入することにある.直接にサブタイプのプロトタイプを親タイプから継承するとChild.prototype = Parent.prototype;、浅いコピーなので、サブタイプのプロトタイプのある属性に対する修正が親タイプに影響する可能性があります.一つの中間変数を使うとこのようなことが起こらないようにします.具体的には前の『Javascript:prototype漫談から継承(1)』を参照してください.
もう一つの考えを変えてみましょう.継承の本質は自分の子供類を父類に訪問できる属性と方法です.one by oneの父類の属性を全部コピーしてもいいです.
function extend2(Child, Parent) {
  var p = Parent.prototype;
  var c = Child.prototype;
  for (var i in p) {
     c[i] = p[i];
  }
  c.uber = p;
}
 
この方法はそんなに優雅ではありません.父の属性を引用してもいいのに、全部コピーしました.しかし、できるだけ引用を減らして、検索の回数を減らしました.
話を始めてから今まで私達の雑談を引き継ぐのはすべて種類と種類で、あるいはコンストラクションとコンストラクションの間の継承ですが、ここはjavascriptです.私達はもう実用化された対象の間の継承を考慮して、上のexted 2を少し改造してください.
function extendCopy(p) {
  var c = {};  
  for (var i in p) {
     c[i] = p[i];
  }
  c.uber = p;
  return c;
}
 
この方法を使うと,入ってきたパラメータは配置関数ではなく,実在のオブジェクトである.これはjqueryの$.extend継承方法の最も基本的な思想です.
 
上の方法、あるいは以上のすべての方法は、深浅コピーの問題を解決していません.以上のすべての方法では、浅いコピーを使用しています.コピーしたのは元の対象のメモリアドレスを指すポインタだけです.もしあなたがサブクラスを修正したら、親タイプのいずれかの属性を継承して、親タイプのいずれかの属性も修正される可能性があります.たとえば:
var c = {};
var p = {
    pro: [1, 2, 3]
}

c.pro = p.pro
c.pro.push(4)

console.log(p.pro) //[1,2,3,4]
 
上記の例では、cのpro属性はp.proから継承され、proは配列タイプですが、c.proを修正すると、pのproも修正されます.これは浅いコピーの危害です.5つの基本データタイプ(Number,String,Boolean,Null,Udefined)以外のデータタイプのコピーはいずれも浅いコピーですが、この危険はやはりサブ属性の操作によって決まります.例えば、子類が親類のある属性から継承されている場合、objectオブジェクト:
var c = {};
var p = {
    name: "lee"
}

c = p;
c = "kill"

console.log(p) 
 
属性全体を再割り当てすると、親の属性は変更されません.その中の原理は、継承の時には、あなたがサブクラスと親タイプのこの属性の指針が同じメモリエリアを指しています.このような修正は、サブクラスのこの属性の指針を修正しただけで、他のところを指していますが、次のように修正すれば問題があります.
var c = {};
var p = {
    name: "lee"
}

c = p;
c.name = "wang"

console.log(p)  // wang
 
そうです.この場合、親類も修正されました.この場合、子類と親類のこの属性はまだ同じメモリ領域を使っています.
この問題を解決する方法も簡単です.上のextendCopy方法を使いますが、各属性をコピーする時、私達は深くコピーする必要があるかどうかをチェックします.必要であれば、深くコピーします.これはjqueryの方法です.でも、もっと厳しい判断をしました.例えば、objectタイプとarrayタイプを判断する時:
function deepCopy(p, c) {
  var c = c || {}; 
  for (var i in p) {
     if (typeof p[i] === 'object') {
      c[i] = (p[i].constructor === Array) ? [] : {};
      deepCopy(p[i], c[i]);
     } else {
      c[i] = p[i];
     } 
  }
  return c;
}
 
実戦では継承以外にこのオブジェクトを再生産したい時に自分の属性を追加すれば、プロトタイプ継承と一対一のコピー属性を同時に使うことができます.
function objectPlus(o, stuff) {
  var n;
  function F() {}
  F.prototype = o;
  n = new F(); 
  //         
  n.uber = o;
  //        
  for (var i in stuff) {
     n[i] = stuff[i];
  }
  return n;
}
 
最後に、大神道グラースの提案した解決策を見てみましょう.
まず彼はObject()の方法を提案しました.
function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}
 
実はこの方法は私達の最初の方法と似ています.本稿の冒頭のexted方法は継承するサブクラスを指定しました.var twoD = {name: "2d shape"}が継承される必要があると仮定して、彼の解決方法はobjectを利用することです.
  • Object方法を利用して、twoDを一つのthatオブジェクトにクローンします.
  • .thatオブジェクトに自分の属性を追加する
  • .
  • は、that
  • に戻る.
    function triangle(s, h) {
      var that = object(twoD);
      that.name ='Triangle';
      that.getArea = function(){return this.side * this.height / 2;};
      that.side = s;
      that.height = h;
      return that;
    }
     
    triangleはただの関数です.構造関数ではなく、入力されたパラメータはカスタマイズされた値です.
     
    基本的に紹介されましたが、構造関数を借りて継承を実現する方法もあります.しかし、個人的には、上記のように多くの方法の引き立ての下で、この方法は明らかではなく、やや難解で、上の方法と十分に使われていると思います.紹介しません.
    最後にjqueryのextedソースを貼りたいですが、コードがちょっと長いです.興味がある学生はgithubのjqueryソースに行ってみてください./src/core.jsファイルの中にあります.
     
    最後にツッコミを入れます.継承の本質は以下の機能を実現したいです.
  • 父類は全部持っています.私も積載できますが、父の属性と方法に影響はありません.
  • 継承以外にも、自分の方法と属性を追加することができます.
    以上の二点ができますが、何を求めていますか?このように多くの方法を紹介しましたが、理解があります.完璧ではないです.全部を参考にして自分の業務ロジックに合うものを組み合わせてもいいです.行動しよう