9

Javascript はプロトタイプベースのプログラミング言語です。Javascript とそのプロトタイプの継承の概念
に関する本をいくつか読んだことがありますが、

「6歳の子に説明できないのは、自分で理解できていないということです。」そういえば、JavaScriptのプロトタイプの概念を22歳の友人に説明しようとしたところ、完全に失敗しました。

そのテーマに妙に興味を持っている6歳の人に、あなたはそれをどのように説明しますか?
Stack Overflow でいくつかの例を見てきましたが、役に立ちませんでした。

4

6 に答える 6

18

古典的な継承とは、物事の型を拡張することです。のようなクラスがあるとしBikeます。動作を拡張したい場合は、新しいタイプの自転車 ( などMotorBike) を設計する必要があります。

それは工場を建設するようなものです。多くの設計図と、それらの設計図を参照する設計図を作成しますが、工場に乗るには、設計図から何かを作成する必要があります。

プロトタイプベースの継承は、物事自体を拡張することです。オブジェクトを作成する方法があるとしBikeます。これらBikeの s の 1 つをガレージに持ち込み、ジェット エンジンをストラップで固定します。

これは設計図によるものではありません。これは、この特定のバイクに対して行ったことです。しかし、あなたの友達はあなたの仕掛けを見て、それも欲しがります。新しいデザインの青写真を作成する代わりに、「JetBike工場」と書かれた看板を立てて、それをさらに作り始めます。そして、何かがどのように組み合わされているか思い出せないときはいつでも、設計図を見る代わりに元のバイクを見るだけです。あなたの元の自転車はプロトタイプの自転車であり、すべての新しい自転車はそれをベースにしています。

さて、本物の 6 歳の子供にとっては、おそらくここでやめてしまいますが (まだそれらを失っていなければ)、実際には、プロトタイプベースの継承はコピーを構築するだけでなく、さらにクールなことを行います。実際に新しいJetBikeオブジェクトを、ガレージにあるオリジナルのプロトタイプ バイクにリンクします。プロトタイプの自転車のサスペンションを交換すると、友達の自転車も魔法のようにサスペンションが交換されます。

JSっぽい疑似コードを見てみましょう:

function Bike() {
    this.wheels = 2;
}
Bike.prototype = {
    ride: function() {
        // Ride the bike
    },
    crash: function() {
        // Fall off the bike
    }
};

function JetBike() {
    this.engines = 2;
}
// Start with an ordinary bike
JetBike.prototype = new Bike();
// Modify it
JetBike.prototype.fly = function () {
    // Engage thrusters and head for the ramp
};
于 2012-08-30T16:52:14.037 に答える
10

他のほとんどのオブジェクト指向言語とは異なり、JavaScript には実際にはクラスの概念がありません。他のほとんどのオブジェクト指向言語では、特定のクラスのインスタンスをインスタンス化しますが、JavaScript ではそうではありません。
JavaScript では、オブジェクトは新しいオブジェクトを作成でき、オブジェクトは他のオブジェクトから継承できます。
この概念全体をプロトタイプ継承と呼びます。

しかし、どうやってオブジェクトを作ることができるでしょうか?
で汎用オブジェクトを作成するだけです{}

var a = {};
a.prop = "myprop";
console.log(a); //Object { prop="myprop" }

関数ではないため、のインスタンスを作成できませんa。つまり、特別な内部メソッドはありません[[Construct]]

JavaScript では、任意の関数をオブジェクトとしてインスタンス化することもできます。以下の関数は、名前を取り、それを現在のコンテキストに保存する単純な関数です。

function User( name ) {
   this.name = name;
}

UserそれがFunction のインスタンスであることがわかります:

alert(User instanceof Function); //true

指定された名前で、その関数の新しいインスタンスを作成します。

var me = new User( "My Name" ); 

nameそれ自体のプロパティとして設定されていることがわかります。

alert( me.name == "My Name" ); //true

そして、それがUserオブジェクトのインスタンスであること:

alert( me.constructor == User ); //true

さて、User()は単なる関数なので、そのように扱うとどうなるでしょうか?

User( "Test" );

thisコンテキストが設定されていないため、デフォルトでグローバルオブジェクトwindowになります。つまり、提供されたものwindow.nameと同じです。name

alert( window.name == "Test" ); //true

プロパティはすべてのconstructorオブジェクトに存在し、それを作成した関数を常に指します。このようにして、オブジェクトを効果的に複製し、同じ基本クラスの新しいオブジェクトを作成できますが、同じプロパティは使用できません。この例を以下に示します。

var you = new me.constructor();

実際、コンストラクターは同じであることがわかります。

alert( me.constructor == you.constructor ); //true

プロトタイプと公開メソッド

プロトタイプには、親オブジェクトのすべての新しいコピーの基本参照として機能するオブジェクトが含まれているだけです。基本的に、プロトタイプのすべてのプロパティは、そのオブジェクトのすべてのインスタンスで使用できます。この作成/参照プロセスにより、継承の安価なバージョンが得られます。
オブジェクト プロトタイプは単なるオブジェクトであるため、他のオブジェクトと同様に、新しいプロパティを追加できます。新しいプロパティをプロトタイプにアタッチすると、元のプロトタイプからインスタンス化されたすべてのオブジェクトの一部になり、すべてのプロパティが効果的に公開されます。例:

function User( name, age ){
   this.name = name;
   this.age = age;
}

メソッドとプロパティをコンストラクター関数のプロトタイプ プロパティに追加することは、このコンストラクターが生成するオブジェクトに機能を追加するもう 1 つの方法です。CardNoもう 1 つのプロパティとgetName()メソッドを追加しましょう。

User.prototype.CardNo='12345';
User.prototype.getName = function(){
  return this.name;
};

そして、プロトタイプに別の関数を追加します。コンテキストがインスタンス化されたオブジェクト内にあることに注意してください。

User.prototype.getAge = function(){
   return this.age;
};

新しいユーザー オブジェクトをインスタンス化します。

var user = new User( "Bob", 44 );

アタッチした 2 つのメソッドが適切なコンテキストでオブジェクトに関連付けられていることがわかります。

alert( user.getName() == "Bob" ); //true
alert( user.getAge() == 44 ); //true

したがって、JavaScript のすべての関数にはプロトタイプ プロパティがあります。その初期値は空のオブジェクト ({}) です。一般的なオブジェクト (関数ではない) にはプロトタイプ プロパティがないことに注意してください。

alert( user.prototype ); //undefined (and it is not useful even you define it)

委任

のプロパティにアクセスしようとするuseruser.name、JavaScript エンジンはオブジェクトのすべてのプロパティを調べて呼び出されたものを探し、name見つかった場合はその値を返します。

alert( user.name );

JavaScript エンジンがプロパティを見つけられない場合はどうなりますか? このオブジェクトを作成するために使用されるコンストラクター関数のプロトタイプを識別します (そうする場合と同じですuser.constructor.prototype)。プロパティがプロトタイプで見つかった場合、このプロパティが使用されます。

alert(user.CardNo); // "12345"

など...
オブジェクト自身のプロパティとプロトタイプのプロパティを区別したい場合は、 を使用しますhasOwnProperty()。試す:

alert( user.hasOwnProperty('name') ); //true
alert( user.hasOwnProperty('CardNo') ); //false

プライベート メソッド

関数のプロパティを直接設定すると、プライベートになります。例:

function User()
{
    var prop="myprop";
    function disp(){
       alert("this is a private function!");
    }
}
var we = new User();
alert(we.prop); //undefined
we.disp(); // Fails, as disp is not a public property of the object

特権メソッド

特権メソッドはDouglas Crockfordによって造られた用語で、(オブジェクト内の) プライベート変数を表示および操作できる一方で、パブリック メソッドとしてユーザーがアクセスできるメソッドを指します。例:
新しいユーザー オブジェクト コンストラクターを作成します。

function User( name, age ) {
   //Attempt to figure out the year that the user was born:
   var year = (new Date()).getFullYear() – age;

   //Create a new Privileged method that has access to the year variable, but is still publically available:
   this.getYearBorn = function(){
      return year;
   };
}

ユーザー オブジェクトの新しいインスタンスを作成します。

var user = new User( "Bob", 44 );

返された年が正しいことを確認します。

alert( user.getYearBorn() == 1962 ); //true

また、オブジェクトのプライベート year プロパティにアクセスできないことに注意してください。

alert( user.year == null ); //true

基本的に、特権メソッドは動的に生成されるメソッドです。これは、コードが最初にコンパイルされたときではなく、実行時にオブジェクトに追加されるためです。この手法は、単純なメソッドをオブジェクト プロトタイプにバインドするよりも計算コストが高くなりますが、はるかに強力で柔軟です。

静的メソッド

静的メソッドの背後にある前提は、他の通常の関数の前提と実質的に同じです。ただし、主な違いは、関数がオブジェクトの静的プロパティとして存在することです。プロパティとして、そのオブジェクトのインスタンスのコンテキスト内ではアクセスできません。これらは、メイン オブジェクト自体と同じコンテキストでのみ使用できます。従来のクラスのような継承に慣れている人にとって、これは静的クラス メソッドのようなものです。
実際には、この方法でコードを記述することの唯一の利点は、オブジェクトの名前空間をきれいに保つことです。
User オブジェクトにアタッチされた静的メソッド:

function User(){}
User.cloneUser = function( user ) {
   //Create, and return, a new user
   return new User( user.getName(), user.getAge() );
};

このcloneUser機能には、次のユーザーのみがアクセスできますUser:

var me = new User();
me.cloneUser(me); //Uncaught TypeError: Object #<User> has no method 'cloneUser' 
于 2012-08-31T04:20:55.553 に答える
3

Javascript は、クラスを持たないという点でユニークなオブジェクト指向言語です。代わりに、関数を使用してオブジェクトを作成します。

すべての関数にはプロトタイプがあり、その関数を使用して作成するすべてのオブジェクトは、すべてのプロパティとメソッドを継承します。JavaScript にはクラスがないため、(クラスではなく) 継承元の実際のオブジェクトを使用して継承を実行します。関数のプロトタイプをオブジェクトに設定すると、その関数で作成したすべてのオブジェクトが、関数のプロトタイプ オブジェクトのすべてのメソッドとプロパティを継承できるようになります。

したがって、オブジェクトを作成する関数がある場合:

function Foo() {

}
Foo.prototype.someProperty = 'blahblahblah';

オブジェクトを作成する別の関数を作成し、関数プロトタイプをそのオブジェクトに設定することで、オブジェクトのプロパティとメソッドを継承できるようにすることができます。

function Bar() {

}
Bar.prototype = new Foo();

その後、継承されたすべてのものにアクセスできます。

var bar = new Bar();
alert( bar.someProperty ); // blahblahblah
于 2012-08-30T17:04:00.047 に答える
1

興味深い挑戦:-)

まず第一に、私は言葉だけでこれを誰にも説明しようとはしませんが、試してみます :-)

「プロトタイプの継承は、他のポケモンから力を盗むことができるポケモンのようなものです。」

自分のポケモンを作成できると考えてください。大きさ、色などを決定できます(コンストラクター)。次に、そのポケモンにパワー(プロトタイプ)を与えることができます。これらのポケモンは好きなだけスポーンできます。プロトタイプの継承によって得られるのは、これらのポケモンの 1 つ、複数、またはすべてが他のポケモンから力を盗む可能性です。すでに他のポケモンから力を盗んでいるポケモンから力を盗むこともできます. これにより、まったく新しい範囲の超強力なポケモンが作成されます。

少しばかげているかもしれませんが、プロトタイプの継承がいかに強力であるかを反映しています...ポケモンの意味で:-)

于 2012-08-30T17:24:55.600 に答える
0
/* Here is simple way how to inherit objects properties and methods from others object by using prototyping inheritance in plain java script.*/
(function() {  
      // get dom elements for display output`enter code here
      var engTeacherPara = document.getElementById("engTeacher");
      var chemTeacherPara = document.getElementById("chemTeacher");
      // base class 
      var SchoolStaff = function(name, id) {
          this.name = name;
          this.id = id;
        }
        // method on the SchoolStaff object
      SchoolStaff.prototype.print = function() {
        return "Name : " + this.name + " Employee id: " + this.id;
      }
    
      SchoolStaff.prototype.sayHello = function() {
        return "Hello Mr : " + this.name;
      }
    
      // sub class engTeacher
      var EngTeacher = function(name, id, salary) {
          SchoolStaff.call(this, name, id);
          this.salary = salary;
        }
        // Inherit the SchoolStaff prototype
      EngTeacher.prototype = Object.create(SchoolStaff.prototype);
    
      // Set the engTeacher constructor to engTeacher object
      EngTeacher.prototype.constructor = EngTeacher;
    
      // method on engTeacher object
      EngTeacher.prototype.print = function() {
        return "Name : " + this.name + " Salary : " + this.salary + " Employee id: " + this.id;
      }
    
      // sub class chemTeacher
      var ChemTeacher = function(name, id, salary, bonus) {
          EngTeacher.call(this, name, id, salary);
          this.bonus = bonus;
        }
        // Inherit the SchoolStaff prototype
      ChemTeacher.prototype = Object.create(EngTeacher.prototype);
    
      // Set the ChemTeacher constructor to ChemTeacher object
      ChemTeacher.prototype.constructor = ChemTeacher;
    
      // method on engTeacher object
      ChemTeacher.prototype.print = function() {
        console.log("Name : " + this.name + " Salary : " + this.salary + " Employee id: " + this.id + " bonus : " + this.bonus);
      }
    
      // create new objcts and check sub class have base class methods access
      var schoolStaff = new SchoolStaff("Base Class", 100);
      console.log(schoolStaff.sayHello()); // Hello Mr : Base Class
    
      var engTeacher = new EngTeacher("Eng Teacher", 1001, 20000);
      engTeacherPara.innerHTML = engTeacher.sayHello(); // Hello Mr : Eng Teacher  
    
      var chemTeacher = new ChemTeacher("Chem Teacher", 1001, 30000, 4000);
      chemTeacherPara.innerHTML = chemTeacher.sayHello(); // Hello Mr : Chem Teacher
    })();
于 2016-11-15T12:46:12.327 に答える