[JavaScript随筆]継承の実現方法と原型の概要


OO言語については、「Everything is oject」という言葉がありますが、JavaScriptは厳密な対象言語ではありませんが、JSの継承を理解するには、この言葉は常に心に刻まれなければなりません.
JSの文法はとても柔軟ですから、簡単だと思う人がいます.どうやって書いても正しいです.また、いくつかの文法のデザインを説明するのが難しいので、なぜtypeof nullがobjectで、typeof undefinedがundefinedなのか教えてもらえますか?また、これはnull==undefinedという前提のもとです.多くの私たちが「わかった」と思っている知識点をよく考えてみると、多くの盲点があります.「無知から恐れない」ということでしょう.
1.シンプルなオブジェクト
相続というからには、最も簡単な対象から言います.
javascriptvar dog = {
  name: 'tom'
}
これは対象の直接量です.オブジェクトの直接量はすべてObjectのサブクラスです.
javascriptdog instanceof Object; // true
2.コンストラクタ
JSの中の構造関数は普通の関数とあまり変わりません.ただ呼び出し時に、前にnewキーワードを加えただけで、構造関数として扱われます.
javascriptfunction Dog(name) {
  this.name = name;
}

var dog = new Dog('tom');

dog instanceof Dog; // true
二つの問題は第一に、newキーワードを使わないとどんな結果がありますか?
Dug関数のthisはコンテキストでグローバル変数と解釈され、具体的にブラウザの端にあるのはwindowオブジェクトで、node環境の下ではglobalオブジェクトです.
第二に、dogの値は何ですか?簡単です.undefined.Dug関数は値を返しませんでした.実行終了後、dogの値は当然undefinedです.
newの過程について、ここで紹介しますが、これはプロトタイプの理解に大きな助けがあります.
  • は、Objectの属性と方法のみを含む空のオブジェクトを作成します.
  • はprototypeの属性と方法を引用して新しいオブジェクトに割り当てます.
  • は、this上の属性と方法を新規に作成し、新しいオブジェクトに割り当てます.
  • (エラー:thisオブジェクトを返し、return文を無視します.)returnの結果がtypeofでobjectとテストされた場合、この結果を返します.さもなければ、thisオブジェクトに戻ります.
  • 具体的には、prototype上の属性と方法は、インスタンス間で共有され、this上の属性と方法は、それぞれのインスタンスに固有であることが必要である.
    3.prototypeの導入
    今はドグ関数にプロトタイプを加えて、例を見ます.
    javascriptfunction Dog(name) {
      this.name = name;
      this.bark = function() {};
    }
    
    Dog.prototype.jump = function() {};
    Dog.prototype.species = 'Labrador';
    Dog.prototype.teeth = ['1', '2', '3', '4'];
    
    var dog1 = new Dog('tom'),
        dog2 = new Dog('jerry');
    
    dog1.bark !== dog2.bark; // true
    dog1.jump === dog2.jump; // true
    
    dog1.teeth.push('5');
    dog2.teeth; // ['1', '2', '3', '4', '5']
    
    コメントを見た3行は「引用」と「新規作成」の違いがわかるはずです.
    よく言われている「プロトタイプチェーン」は何ですか?この用語は、オブジェクトのインスタンスにおける属性および方法がどこから来るかを示す継承において現れる.はい、これは筆者の説明です.
    javascript- Object
      bark: Dog/this.bark()
      name: 'tom'
    - __proto__: Object
        jump: Dog.prototype.jump()
        species: 'Labrador'
      + teeth: Array[4]
      + constructor: Dog()
      + __proto__: Object  
    
    上にあるのはdog 1のプロトタイプチェーンです.「チェーン」という概念を直観的に説明するには十分ではないと思います.
  • のうち、barkとnameはthisの中で定義されているので、最上階には2人が見られます.
  • その後、どのオブジェクトにも一つの_uがあります.プロト.属性(IE 11+)は、プロトタイプに定義されている属性と方法を表していますので、jump、species、teethはもちろんここにあります.
  • 最後にずっと上を探しています.プロト.の属性と方法を指定します.
  • 4.継承の幾つかの実現
    4.1 callまたはappyによる
    プログラミングには引き継ぎの言い方が二つあります.一つはinheitといい、もう一つはextedです.前者は厳格な意味での継承であり、即ち親子関係があり、後者は一つの種類だけで別のクラスの属性と方法を拡張している.callとappyは後者のカテゴリーです.どういうことですか
    javascriptfunction Animal(gender) {
      this.gender = gender;
    }
    
    function Dog(name, gender) {
      Animal.call(this, gender);
      this.name = name;
    }
    
    var dog = new Dog('tom', 'male');
    
    dog instanceof Animal; // false
    
    dogオブジェクトにはgender属性がありますが、dogはAnimalタイプではありません.さらに、このような方法は、父類がthisで定義した属性と方法を「継承」するだけで、Animal.prototypeの属性と方法を継承することはできません.
    4.2 prototypeによる継承実現
    継承を実現するには、「原型」という概念が必要です.以下はよく使われる継承方式です.
    javascriptfunction Dog(name) {
      Animal.call(this);
    }
    
    Dog.prototype = new Animal(); //     Animal       
    Dog.prototype.constructor = Dog;
    
    var dog = new Dog('tom');
    
    dog instanceof Animal; // true
    
    継承の結果は二つあります.一つは父の属性と方法を獲得します.二、instance ofのテストを正しく通過する.
    prototypeも対象です.例を作る時の組立機です.これは前に述べました.new Animal()の値はAnimalのインスタンスのすべての属性と方法を含み、Docgのprototypeを与えた以上、Docgのインスタンスは自然に親の属性と方法のすべてを獲得する.
    また、この例を通して、Docgのprototype属性を変えることでinstance ofのテスト結果、つまり親のタイプが変更されることが分かります.
    そして、なぜDocgの構造関数でAnimal.calを呼び出すのですか?
    Animalでは、thisで方法と関数を定義しているかもしれませんので、この文がないと、これらはすべてDocgのprototypeに与えられます.前の知識から、prototypeの属性と方法は実例の間で共有されています.
    これらの属性と方法を共有ではなくインスタンス自体の空間に残したいので、書き換えが必要です.
    なぜconstructorを修正したのかというと、プロトタイプのチェーンを正しく表示するためというしかないでしょう.instance ofの判断に影響はありません.あるいは他の深い道理があるかもしれません.分かりません.
    4.3空のオブジェクトを利用して相続を実現する
    上の継承方式はもう完璧に近いです.2点を除きます.
    一、Animalには構造パラメータがありますが、これらのパラメータを使ってどうすればいいですか?二、Docg.prototypeにAnimalの例で冗長性を定義する属性と方法が追加されました.
    javascriptfunction Animal(name) {
      name.doSomething();
    }
    
    function Dog(name) {
      Animal.call(this, name);
    }
    
    Dog.prototype = new Animal(); //       name  ,   Animal      ,   
    Dog.prototype.constructor = Dog;
    
    この問題は空のオブジェクトで解決できます.
    javascriptfunction DummyAnimal() {}
    DummyAnimal.prototype = Animal.prototype;
    
    Dog.prototype = new DummyAnimal();
    Dog.prototype.constructor = Dog;
    
    彼の原始的な方法は次のobjectである.
    javascriptfunction object(o) {
      function F() {}
      F.prototype = o;
      return new F();
    }
    
    Dog.prototype = object(Animal.prototype);
    Dog.prototype.constructor = Dog;
    
    4.4_4.4を利用するプロト.継承を実現する
    今はもう一つの問題しか残っていません.どうやって冗長属性と方法を削除しますか?
    実は、第3小節から原型を紹介した時に、__u u uプロト.属性、instance of演算子は、あるタイプに属するかどうかを判定します.
    だから私達はこのように継承できます.
    javascriptfunction Dog() {
      Animal.call(this);
    }
    
    Dog.prototype = {
      __proto__: Animal.prototype,
      constructor: Dog
    };
    
    互換性を考慮しないなら、これはOOの観点から最も適切な継承方式であるべきです.
    4.5コピー引継ぎ
    この方式はinheitではなくextedと呼ぶしかないので、話す必要もないです.
    Backbone.Model.exted、jQuery.extedまたは_.extedはコピーして継承しています.それらがどのように実現されたのかを少し見てもいいです.(または自分でよく検討してから、この部分を補いに来ましょう)
    5.個人結婚
    継承の実現方法を議論していると、孔乙己が「ウイキョウ」を誇示している「ウイキョウ」という表記がいくつかあるような気がします.継承はJSの中で大きな内容を占めていますので、多くのライブラリは自分の実現方法があります.それらは私が考えている「最も適切な」方法を使っていません.なぜですか?JSはJSです.生まれつき柔軟に設計されていますので、なぜこの特性を利用しないで、OOのやり方を強くしなければならないですか?
    継承を通じて、私たちは父の属性と方法を獲得したいです.厳格な父の種類/子の関係を保証するかどうかは、気にしないでコピーして相続することが多いです.プロトタイプの継承については、コードの中で様々なタイプがfunctionで定義されていますが、コピーはより一般的です.オブジェクトの属性と方法を他のオブジェクトにコピー(拡張)するだけです.プロトタイプチェーンは何ですか?
    もちろん、コピーの継承がどれほどいいかを吹聴した時、原型に基づく継承は自然とかけがえのない理由があります.具体的な問題は具体的に分析しなければなりません.具体的な使用シーンが決まっていない時、一番いい方法がありません.
    個人的な見解は、もっと理解してもらうのが一番いいです.何か間違いがあれば、よろしくお願いします.