web-dev-qa-db-ja.com

JavaScriptのプロトタイプとコンストラクターの違いを理解しよう

私はJavaScriptが初めてで、この概念を理解するために、プロトタイプとコンストラクターに関する多くの記事を読みましたが、どこに行っても混乱が残ります。

混乱は、人々がコンストラクターとプロトタイプについて同時に話すときに発生します。

次の例では

_var employee = function Emp(name) {
    this.name = name;
}
var jack = new employee("Jack Dwain");

employee.constructor //gives Function()

employee.prototype // gives  Emp {}

employee.prototype.constructor //gives Emp(name)

jack.constructor //gives Emp(name)

jack.prototype //gives undefined
_
  1. プロトタイプはJSが継承を実現する方法です。Emp(name)は基本関数プロトタイプであるため、同じ関数自体を参照しています。それは何が起こったのですか?

  2. _employee.constructor_と_employee.prototype.constructor_の違いは何ですか?

  3. _jack.prototype_がundefinedだったのはなぜですか?つまり、関数Emp(name)から継承している場合、なぜその関数を参照しなかったのですか?

  4. プロトタイプ、コンストラクタ、またはprototype.constructorが生成するものをコンソールに入力せずに、自分自身を明確に予測するにはどうすればよいですか?

42
Ein2012

他のOOP言語でオブジェクトを簡単に拡張できることに慣れている場合、心を包み込むのはかなり難しいことですが、それらの使用法とその内容を説明するために最善を尽くしますあなたは他のOOP言語に精通していると思います。私が間違っているなら私を修正してください。

すべての関数にはプロトタイプFunction()があります。 toString()やvalueOf()などのFunctionからすべての基本機能を継承しています。

次に、コンストラクターがあります。それがオブジェクトの初期化に使用するものです。

p = new Foo();

したがって、この場合には2つのことがあります。

  • Functionをプロトタイプとする_function Foo_(Foo)
  • Constructor(p)としてFoo()を持つFunctionオブジェクト

(まだ私をフォローしていますか?)

Foo()コンストラクターは、Functionコンストラクターのいくつかの基本機能をオーバーライドできますが、それをそのままにして有効に活用することもできます。

OOPの原則に精通している場合、プロトタイプは基本クラスであり、コンストラクタは現在のクラスです。OOP上記は_class Foo extends Function_

機能を共有しながら、プロトタイプとコンストラクターのこのセットアップ全体で継承を開始し、より複雑なオブジェクトを作成することもできます。

例えばこれは:

_// make a object initialiser extending Function. in oop `class Foo extends Function`

function Foo(bar) {
    this.baz = bar;
}
Foo.prototype.append = function(what) {
    this.baz += " " + what;
};
Foo.prototype.get() {
    return this.baz
}
_

では、そこからbazを取得するさまざまな方法が必要だとしましょう。 1つはコンソールロギング用で、もう1つはタイトルバーに表示するためのものです。私たちのクラスFooについて大きなことをすることはできますが、新しいクラスではまったく異なることをする必要がありますが、異なる実装のために作られているため、私たちはそれをしません。彼らが共有する必要があるのは、bazアイテムとセッターとゲッターだけです。

したがって、OOP項を使用するように拡張する必要があります。OOpでは、これが目的の最終結果class Title extends Foo(){}になります。そのための方法を見てみましょう。

_function Title(what) {
    this.message = what;
}
_

この時点で、Title関数は次のようになります。

  • プロトタイプ機能
  • コンストラクターのタイトル

したがって、Fooを拡張するには、プロトタイプを変更する必要があります。

_Title.prototype = new Foo();
_
  • プロトタイプフー
  • コンストラクターFoo

これは、プロトタイプに対して新しいFoo()オブジェクトを初期化することにより行われます。これは基本的に、タイトルと呼ばれるFooオブジェクトです。タイトルのメッセージ部分にアクセスできないため、これは望んでいることではありません。コンストラクターをTitleにリセットすることで、Foo()を適切に拡張できます。

_Title.prototype.constructor = Title;
_
  • プロトタイプフー
  • コンストラクターのタイトル

今、もう一つの問題に直面しています。 Fooのコンストラクターは初期化されないため、未定義の_this.baz_になります

それを解決するには、親を呼び出す必要があります。 Javaではsuper(vars)を使用して、php $parent->__construct($vars)を使用します。

Javascriptでは、親オブジェクトのコンストラクターを呼び出すためにTitleクラスコンストラクターを変更する必要があります。

したがって、Titleクラスのコンストラクタは次のようになります。

_function Title(what) {
    Foo.call(this,what);
    this.message = what;
}
_

Fooが継承したFunctionオブジェクトプロパティを使用することで、TitleオブジェクトのFooオブジェクトを初期化できます。

これで、適切に継承されたオブジェクトができました。

したがって、他のOOP言語のようなextendのようなキーワードを使用する代わりに、prototypeおよびconstructorを使用します。

32
Tschallacka

employee.constructor // Function()を提供します

JavaScriptでは、関数もオブジェクトであり、独自のコンストラクタ Function を使用して構築できます。したがって、次のコードを記述して、Functionのインスタンスを取得できます。

var employee2 = new Function('a', 'b', 'return a+b');

あなたの場合のように関数リテラルを使用して関数を作成するときにも同じことが起こります。また、このオブジェクトのコンストラクタープロパティは、同じネイティブ関数オブジェクト/クラスも参照します。

employee.prototype // Emp {}を提供します

JavaScriptの各オブジェクトには、プロトタイプが関連付けられています。ただし、.prototypeで直接アクセスできるのは関数オブジェクトプロトタイプのみです。 newキーワードを使用してオブジェクトを作成すると、この同じプロトタイプがオブジェクトプロトタイプにコピーされます。主に、このコピーが継承/拡張の原因となります。プロトタイプはコピーされますが、Functionオブジェクトの場合のように直接分割可能ではありません。 .__proto__を使用すると、非標準的な方法で利用できます。次のコードはtrueを返します。

jack.__proto__==employee.prototype

employee.prototype.constructor // Emp(name)を提供します

Object.prototype.constructor のドキュメントで述べたように。これにより、インスタンスのプロトタイプを作成したObject関数への参照が返されます。ここでは、参照されるオブジェクトはemployee.prototypeとnot employeeです。これは少し複雑ですが、オブジェクトemployee.prototypeのプロトタイプは関数Emp(name)によって作成されました

jack.constructor // Emp(name)を提供します

前のポイントで述べたように、このオブジェクトプロトタイプは、new Emp()を使用してオブジェクトを作成したときに、関数Emp(name)によって作成されました。

jack.prototype //未定義になります

jackは関数オブジェクトではないため、そのようなプロトタイプにアクセスすることはできません。以下のようなジャックのプロトタイプにアクセスできます(標準ではありません)。

jack.__proto__
8
Vishwanath

Javascript objectを作成する場合は、新しいオブジェクトを宣言してプロパティを指定するだけで済みます(自分自身をオブジェクト化することにしました)。

var myself= {
    name:"Niddro",
    age:32
};

このメソッドを使用すると、oneオブジェクトを作成できます。欲しいものがprototypeである場合、一般的な人を表し、同じ設定で複数の人を宣言できます。プロトタイプを作成するには、以下に示すように、constructorを使用できます。

//Constructor
function generalNameForObject(param1, param2,...) {
    //Give the object some properties...
}

Personを呼び出したいというプロトタイプ(recepie)があり、プロパティの名前と年齢を含める必要があり、コンストラクターを使用して作成します。

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

上記の構成関数は、私の個人オブジェクトのプロトタイプを記述しています。

構成関数を呼び出して、新しい人を作成します。

var myself = new person("Niddro",31);
var OP = new person("rajashekar thirumala",23);

時間が経ち、私は誕生日を迎えたことに気づいたので、プロトタイプのプロパティを変更する必要があります。

myself.age=32;

コンストラクトにプロパティを追加するが必要な場合は、コンストラクト関数に手動で追加する必要があります。

function person(name,age,rep) {
    this.name=name;
    this.age=age;
    this.reputation=rep;
}

代わりに、次の操作を実行して、プロトタイプにプロパティを追加できます(ここで、「プロトタイプ」は単なる名前ではなく実際のコマンドです)。

function person(name,age,rep) {
    this.name=name;
    this.age=age;
}
person.prototype.reputation=105;

これにより、作成されたすべてのオブジェクトの評価が105になることに注意してください。

これにより、コンストラクタとプロトタイプの関係についてより多くの洞察が得られたと思います。

5
Niddro

コンストラクタ:

_function Foo(x) {
    this.x =x;
}
_

Fooはコンストラクターです。コンストラクターは関数です。

このコンストラクターFooを使用するには2つの方法があります。

「オブジェクトは、新しい式でコンストラクタを使用して作成されます。たとえば、new Date(2009,11)は、新しいDateオブジェクトを作成します。newを使用せずにコンストラクタを呼び出すと、コンストラクタに依存する結果になります。オブジェクトではなく現在の日付と時刻の表現。」

ソース ECMA-262

つまり、Fooが(_return "somevalue";_を介して)何かを返す場合、typeof Foo()が戻り値の型になります。

一方、あなたが電話するとき

_var o = new Foo();
_

JavaScriptは実際には

_var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);
_

プロトタイプ:

_o.a_を呼び出すと、javascriptは最初にaがオブジェクトoの独自のプロパティであるかどうかをチェックします。そうでない場合、javascriptはaを見つけるためにプロパティチェーンを検索します。

プロパティチェーンの詳細については、 mdn をご覧ください。

コンストラクタのprototype porpertyには、クラスで使用できない非常に強力な機能があります。役に立つかどうかは別の議論です。コンストラクタのprototype porpertyは、プロトタイプチェーン内のそのプロトタイプにリンクする各インスタンスのプロパティを変更できます。

要約:

注:これは正確な定義ではありません。概要の目的は、コンストラクターとプロトタイプについての感触を伝えることです。

newキーワードを使用してコンストラクターを使用する場合、コンストラクターとプロトタイプは、完全に異なっていても、似たような目的を持っています。コンストラクターはオブジェクトのプロパティを初期化するため、プロパティを提供します。プロトタイプは、プロパティチェーンを介してプロパティも提供します(プロトタイプベースの継承)。

3
user8175891

しかし真実は、このアプローチは多くの状況で間違っているかもしれません。 JavaScriptでメソッドをthisキーワードにバインドすると、そのメソッドはその特定のインスタンスにのみ提供され、静的メソッドのように、そのコンストラクターのオブジェクトインスタンスとは実際には関係がありません。関数はJavascriptの最上級の市民であることに留意して、オブジェクトのように扱うことができます。この場合は、関数オブジェクトのインスタンスにプロパティを追加するだけです。これはストーリーの一部にすぎません。また、これを介してアタッチされたメソッドは、作成する新しいインスタンスごとに再宣言されることを知っておく必要があります。

0
abdulwahhab