javascript - 継承 - js prototype 書き方
JavaScriptの.prototypeはどのように機能しますか? (16)
私はそれが動的プログラミング言語ではありませんが、私はJavaScriptコードのかなりの部分を書いています。 このプロトタイプベースのプログラミングの周りに私の頭を浮かべることは決してありません。どのように動作するか知っていますか?
var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();
私は人々としばらく話していたことを覚えています(私は何をしているのか正確にはわかりませんが)私が理解するように、クラスの概念はありません。 それは単なるオブジェクトであり、それらのオブジェクトのインスタンスは元のクローンです。
しかし、JavaScriptのこの ".prototype"プロパティの正確な目的は何ですか? オブジェクトのインスタンス化とはどのように関連していますか?
更新:正しい方法
var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!
function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK
また、これらのslides本当に助けになりました。
この ".prototype"プロパティの正確な目的は何ですか?
標準クラスへのインタフェースは拡張可能になります。たとえば、Array
クラスを使用しており、すべての配列オブジェクトにカスタムシリアライザを追加する必要があります。あなたは、サブクラスをコーディングする時間を費やすか、コンポジションを使用するか、または... prototypeプロパティは、ユーザーがクラスで使用可能なメンバー/メソッドの正確なセットを制御できるようにすることでこれを解決します。
プロトタイプを特別なvtableポインタと考えてください。一部のメンバーが元のクラスにない場合、プロトタイプは実行時に参照されます。
プロトタイプの7人のKoans
Ciro Sanが深い瞑想の後にMount Fire Foxを下ったとき、彼の心ははっきりと平穏だった。
しかし、彼の手は落ち着きがなく、それだけでブラシをつかんで、次の注意を書き留めました。
0) 2つの異なるものを「プロトタイプ」と呼ぶことができます:
obj.prototype
プロトタイププロパティES5で
[[Prototype]]
と表されるプロトタイプの内部プロパティ。これは、ES5の
Object.getPrototypeOf()
を介して取得できます。Firefoxでは、拡張子として
__proto__
プロパティを使用してアクセスできます。 ES6では 、__proto__
いくつかのオプション要件について言及しています。
1)これらの概念は、質問に答えるために存在する:
obj.property
を実行すると、JSはどこで.property
探すのですか?
直観的には、古典的な継承はプロパティの検索に影響するはずです。
2)
-
__proto__
はドットに使用され.
obj.property
ようなプロパティ参照。 -
.prototype
は、オブジェクトの作成時にnew
で.prototype
決定するため、間接的にのみルックアップに直接使用されません 。
検索順序は次のとおりです。
-
obj.p = ...
またはObject.defineProperty(obj, ...)
追加されたobj
プロパティ -
obj.__proto__
プロパティ -
obj.__proto__.__proto__
などのプロパティ - いくつかの
__proto__
がnull
場合、undefined
返します。
これは、いわゆるプロトタイプチェーンです。
あなたは避けることができ.
obj.hasOwnProperty('key')
とObject.getOwnPropertyNames(f)
ルックアップ
3) obj.__proto__
を設定する主な方法は2つあります。
new
:var F = function() {} var f = new F()
new
が設定されました:f.__proto__ === F.prototype
これは
.prototype
が使用される場所.prototype
。Object.create
:f = Object.create(proto)
セット:
f.__proto__ === proto
4)コード:
var F = function() {}
var f = new F()
次の図に対応します。
(Function) ( F ) (f)
| ^ | | ^ |
| | | | | |
| | | | +-------------------------+ |
| |constructor | | | |
| | | +--------------+ | |
| | | | | |
| | | | | |
|[[Prototype]] |[[Prototype]] |prototype |constructor |[[Prototype]]
| | | | | |
| | | | | |
| | | | +----------+ |
| | | | | |
| | | | | +-----------------------+
| | | | | |
v | v v | v
(Function.prototype) (F.prototype)
| |
| |
|[[Prototype]] |[[Prototype]]
| |
| |
| +-------------------------------+
| |
v v
(Object.prototype)
| | ^
| | |
| | +---------------------------+
| | |
| +--------------+ |
| | |
| | |
|[[Prototype]] |constructor |prototype
| | |
| | |
| | -------------+
| | |
v v |
(null) (Object)
この図は、多くの言語定義済みのオブジェクトノードを示していますObject.prototype
、 Object
、 Object.prototype
、 Function
およびFunction.prototype
。 私たちの2行のコードは、 f
、 F
およびF.prototype
作成しただけです。
5) F.prototype
は通常F.prototype
から.
見上げる:
f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor
f.constructor
を書くf.constructor
、JavaScriptはJavaScriptを実行し.
ルックアップ:
-
f
は.constructor
持たない -
f.__proto__ === F.prototype
はf.__proto__ === F.prototype
.constructor === F
があるので、それを取る
f.constructor == F
の結果は直感的に正しい。なぜならF
は古典的なOOP言語のようにf
を設定するために使われるからである。
6)古典的継承構文は、プロトタイプチェーンを操作することによって達成することができる。
ES6はclass
とextends
キーワードを追加します。これは以前に考えられていたプロトタイプ操作の狂気のためのシンタックスシュガーです。
class C {
constructor(i) {
this.i = i
}
inc() {
return this.i + 1
}
}
class D extends C {
constructor(i) {
super(i)
}
inc2() {
return this.i + 2
}
}
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// http://.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined
定義済みのすべてのオブジェクトがない単純化された図:
__proto__
(C)<---------------(D) (d)
| | | |
| | | |
| |prototype |prototype |__proto__
| | | |
| | | |
| | | +---------+
| | | |
| | | |
| | v v
|__proto__ (D.prototype)
| | |
| | |
| | |__proto__
| | |
| | |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype
Javascriptには通常の意味での継承はありませんが、プロトタイプチェーンがあります。
プロトタイプチェーン
オブジェクトのメンバーがオブジェクト内に見つからない場合は、プロトタイプチェーン内でそれを探します。 チェーンは他のオブジェクトで構成されています。 指定されたインスタンスのプロトタイプには、 __proto__
変数を使用してアクセスできます。 javascriptのクラスとインスタンスには違いがないため、すべてのオブジェクトに1つがあります。
プロトタイプに関数/変数を追加する利点は、すべてのインスタンスではなく、一度だけメモリ内になければならないということです。
プロトタイプチェーンは他の多くのオブジェクトで構成できるため、継承にも役立ちます。
Java、C#、C ++などの古典的継承を実装する言語では、オブジェクトの青写真であるクラスを作成してから、そのクラスから新しいオブジェクトを作成したり、クラスを拡張したり、元のクラス。
JavaScriptではまずオブジェクトを作成します(クラスの概念はありません)。次に、独自のオブジェクトを補強したり、新しいオブジェクトを作成したりすることができます。 それは困難ではありませんが、古典的な方法で慣れた誰かのために、少し外国語で難しいです。
例:
//Define a functional object to hold persons in JavaScript
var Person = function(name) {
this.name = name;
};
//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
return this.name;
};
//Create a new object of type Person
var john = new Person("John");
//Try the getter
alert(john.getName());
//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
alert('Hello, my name is ' + this.getName());
};
//Call the new method on john
john.sayMyName();
これまでは基本オブジェクトを拡張していましたが、今度は別のオブジェクトを作成してPersonから継承します。
//Create a new object of type Customer by defining its constructor. It's not
//related to Person for now.
var Customer = function(name) {
this.name = name;
};
//Now I link the objects and to do so, we link the prototype of Customer to
//a new instance of Person. The prototype is the base that will be used to
//construct all new instances and also, will modify dynamically all already
//constructed objects because in JavaScript objects retain a pointer to the
//prototype
Customer.prototype = new Person();
//Now I can call the methods of Person on the Customer, let's try, first
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();
//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
return this.amountDue;
};
//Let's try:
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());
var Person = function (name) {
this.name = name;
};
Person.prototype.getName = function () {
return this.name;
};
var john = new Person("John");
alert(john.getName());
Person.prototype.sayMyName = function () {
alert('Hello, my name is ' + this.getName());
};
john.sayMyName();
var Customer = function (name) {
this.name = name;
};
Customer.prototype = new Person();
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();
Customer.prototype.setAmountDue = function (amountDue) {
this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function () {
return this.amountDue;
};
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());
一方、私はsetAmountDue()、getAmountDue()をPersonで呼び出すことはできません。
//The following statement generates an error.
john.setAmountDue(1000);
prototype
使用すると、クラスを作成できます。 prototype
を使用しないと、静的になります。
ここに簡単な例があります。
var obj = new Object();
obj.test = function() { alert('Hello?'); };
上記の場合、静的関数呼び出しテストがあります。 この関数は、objがクラスであると想像できるobj.testだけがアクセスできます。
以下のコードのように
function obj()
{
}
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();
objはインスタンス化できるクラスになりました。 objの複数のインスタンスが存在する可能性があり、すべてがtest
関数を持っています。
上記は私の理解です。 私はそれをコミュニティのwikiにしているので、私が間違っていると人々が私を修正することができます。
私はJavaScriptの教師としての役割を果たしています。プロトタイプのコンセプトは、私が教えるときは常に議論の対象となっています。 この概念を明確にするための良い方法を考え出すのにはしばらく時間がかかりましたが、今度はこのテキストでJavaScriptの.prototypeの仕組みを説明しようとしています。
これは非常に単純なプロトタイプベースのオブジェクトモデルであり、解説の間にサンプルとして考えられ、コメントはまだありません。
function Person(name){
this.name = name;
}
Person.prototype.getName = function(){
console.log(this.name);
}
var person = new Person("George");
プロトタイプのコンセプトを検討する前に検討しなければならない重要な点がいくつかあります。
1- JavaScript関数の実際の動作
まず最初に、JavaScript関数が実際にどのように機能するか、 this
キーワードを使った関数のようなクラスとして、あるいは引数を持つ通常の関数として、それが何を返し、何を返すかを把握する必要があります。
Person
オブジェクトモデルを作成したいとしましょう。 このステップでは、 prototype
とnew
キーワードを使用せずに同じことをやろうとしています。
だから、このステップでは、 functions
、 objects
、そしてthis
キーワードはすべて私たちが持っています。
最初の質問は、 this
キーワードがnew
キーワードを使用せずにどのように役立つかということです 。
そこで、空のオブジェクトがあり、次のような2つの関数があると答えてみましょう:
var person = {};
function Person(name){ this.name = name; }
function getName(){
console.log(this.name);
}
今はnew
キーワードを使用せずにこれらの関数をどのように使用できるかを示します。 JavaScriptには3つの方法があります:
a。 最初の方法は、関数を通常の関数として呼び出すことです。
Person("George");
getName();//would print the "George" in the console
この場合、これは現在のコンテキストオブジェクトになります。通常はブラウザのグローバルwindow
オブジェクトまたはNode.js
GLOBAL
です。 つまり、ブラウザーにwindow.name、Node.jsにGLOBAL.nameがあり、その値に "George"が入ります。
b。 それらをオブジェクトのプロパティとしてアタッチすることができます
- これを行う最も簡単な方法は、空のperson
オブジェクトを変更することです:
person.Person = Person;
person.getName = getName;
このように、私たちは次のように呼び出すことができます:
person.Person("George");
person.getName();// -->"George"
今、 person
オブジェクトは次のようになります。
Object {Person: function, getName: function, name: "George"}
- オブジェクトにプロパティを付けるもう1つの方法は、 __proto__
という名前のJavaScriptオブジェクトで見つかるオブジェクトのprototype
を使用することです。これをサマリー部分で少し説明しようとしました。 だから私たちは次のようにして同様の結果を得ることができました:
person.__proto__.Person = Person;
person.__proto__.getName = getName;
しかし 、私たちが実際にやっていることは、リテラル( { ... }
)を使ってJavaScriptオブジェクトを作成するたびにObject.prototype
に基づいて作成されるObject.prototype
、 Object.prototype
を変更することです。オブジェクトを__proto__
という名前の属性として使用するので、前のコードスニペットで行ったように変更すると、すべてのJavaScriptオブジェクトが変更されます。 だから今よりよい練習になることができます:
person.__proto__ = {
Person: Person,
getName: getName
};
今は他のオブジェクトは平和ですが、それでも良い習慣ではないようです。 だから私たちはまだ解決策がもう1つありますが、このソリューションを使うためには、 person
オブジェクトが作成されたコード行( var person = {};
)に戻り、次のように変更する必要があります:
var propertiesObject = {
Person: Person,
getName: getName
};
var person = Object.create(propertiesObject);
それは新しいJavaScript Object
を作成し、 propertiesObject
を__proto__
属性にアタッチするpropertiesObject
です。 だからあなたができることを確認する:
console.log(person.__proto__===propertiesObject); //true
しかしここでの__proto__
は、 person
オブジェクトの最初のレベルで__proto__
で定義されているすべてのプロパティにアクセスできることです(詳細については、要約部分を参照してください)。
これらの2つの方法のいずれかを使用すると、 this
はperson
オブジェクトを正確に指し示します。
c。 JavaScriptには、 this
関数に関数を呼び出すための別の方法があります。これは、関数を呼び出すためにcallまたはapplyを使用しています。
apply()メソッドは、与えられた値と引数が配列(または配列のようなオブジェクト)として提供された関数を呼び出します。
そして
call()メソッドは、与えられた値と引数を個別に提供する関数を呼び出します。
私のお気に入りであるこの方法で、簡単に次のような関数を呼び出すことができます:
Person.call(person, "George");
または
//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);
getName.call(person);
getName.apply(person);
これらの3つの方法は、.prototype機能を理解するための重要な初期ステップです。
2 - new
キーワードはどのように機能しますか?
これは.prototype
機能を理解するための2番目のステップです。これは私がプロセスをシミュレートするために使用するものです:
function Person(name){ this.name = name; }
my_person_prototype = { getName: function(){ console.log(this.name); } };
この部分では、 new
キーワードを使用するときに、 new
キーワードとprototype
を使用せずに、JavaScriptが取るすべての手順を実行しようとしています。 new Person("George")
、 Person
関数がコンストラクタとして機能しますPerson
機能は次のとおりです。
a。 まず空のオブジェクトを作成します。基本的に空のハッシュは次のようになります。
var newObject = {};
b。 JavaScriptが取る次のステップは、すべてのプロトタイプオブジェクトを新しく作成されたオブジェクトにアタッチすることです
私たちはここにprototypeオブジェクトに似たmy_person_prototype
を持っています。
for(var key in my_person_prototype){
newObject[key] = my_person_prototype[key];
}
JavaScriptがプロトタイプで定義されているプロパティを実際にアタッチする方法ではありません。 実際の方法はプロトタイプのチェーン概念に関連しています。
a。 &b。 これらの2つのステップの代わりに、以下のようにして同じ結果を得ることができます。
var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"
これでmy_person_prototype
getName
関数を呼び出すことができます:
newObject.getName();
c。 そのオブジェクトをコンストラクタに渡し、
次のようなサンプルでこれを行うことができます:
Person.call(newObject, "George");
または
Person.apply(newObject, ["George"]);
そのコンストラクタは、作成したばかりのオブジェクトであるため、コンストラクタは必要な処理を実行できます。
他のステップをシミュレートする前の最終結果:Object {name: "George"}
概要:
基本的に、関数上でnewキーワードを使用すると、それを呼び出すことになり、その関数はコンストラクタとして機能します。
new FunctionName()
JavaScriptは内部的にオブジェクトを作成し、空のハッシュを作成し、そのオブジェクトをコンストラクタに渡すと、コンストラクタは作成したオブジェクトであるため、コンストラクタは必要な処理を実行できます。関数内でreturn文を使用していない場合、またはreturn undefined;
にした場合はreturn undefined;
あなたの機能体の終わりに。
したがって、JavaScriptがオブジェクトのプロパティをルックアップする場合、最初に実行するのは、そのオブジェクトをルックアップすることです。 そして、秘密のプロパティ[[prototype]]
これは通常、 __proto__
が好きで、そのプロパティはJavaScriptが次に見えるものです。 また、 __proto__
調べるときは、別のJavaScriptオブジェクトである限り、独自の__proto__
属性を持ちます。次の__proto__
がnullになるまで、 __proto__
属性は上がります。 このObject.prototype
属性がnullである唯一のJavaScript Object.prototype
オブジェクトは、 Object.prototype
オブジェクトです。
console.log(Object.prototype.__proto__===null);//true
それがJavaScriptでの継承の仕組みです。
言い換えれば、関数のプロトタイププロパティを持っていて、新しいものを呼び出すと、そのプロパティのために新しく作成されたオブジェクトをJavaScriptが終了した後に、その関数の.prototype
を見て、このオブジェクト独自の内部プロトタイプを持っています。 等々。
ここには2つの異なるが関連するエンティティがあり、説明が必要です。
.prototype
関数のプロパティ。- すべてのオブジェクト[2]の
[[Prototype]]
[1]プロパティ。
これらは2つの異なるものです。
[[Prototype]]
プロパティ:
これはすべての[2]オブジェクトに存在するプロパティです。
ここに格納されているものは、オブジェクト自体として[[Prototype]]
別のオブジェクトを指す別のオブジェクトです。その他のオブジェクトには[[Prototype]]
独自のものがあります。このストーリーは、すべてのオブジェクト(のような.toString
)でアクセス可能なメソッドを提供するプロトタイプオブジェクトに到達するまで続きます。
[[Prototype]]
プロパティは、形成するものの一部である[[Prototype]]
チェーンを。このチェーン[[Prototype]]
オブジェクトは、例えば、場合、検査されているものである[[Get]]
か、[[Set]]
操作がオブジェクトに実行されます。
var obj = {}
obj.a // [[Get]] consults prototype chain
obj.b = 20 // [[Set]] consults prototype chain
.prototype
プロパティ:
これは、関数上にのみ存在するプロパティです。非常に単純な関数を使う:
function Bar(){};
この.prototype
プロパティは、オブジェクトを保持しているb.[[Prototype]]
ときに割り当てられますvar b = new Bar
。これを簡単に調べることができます:
// Both assign Bar.prototype to b1/b2[[Prototype]]
var b = new Bar;
// Object.getPrototypeOf grabs the objects [[Prototype]]
console.log(Object.getPrototypeOf(b) === Bar.prototype) // true
最も重要なの一つ.prototype
sがあるということObject.prototype。このプロトタイプは、すべての[[Prototype]]
チェーンに含まれるプロトタイプのオブジェクトを保持します。その上に、新しいオブジェクトの利用可能なメソッドがすべて定義されます:
// Get properties that are defined on this object
console.log(Object.getOwnPropertyDescriptors(Object.prototype))
さて、.prototype
オブジェクトであるので、それは[[Prototype]]
プロパティを持っています。あなたがへの割り当てを行わない場合はFunction.prototype
、.prototype
さんの[[Prototype]]
プロトタイプオブジェクトへのポイントを(Object.prototype
)。これは、新しい機能を作成するたびに自動的に実行されます。
このようにしてnew Bar;
、プロトタイプチェーンが設定されると、定義されたすべてと定義されたすべてが取得Bar.prototype
されObject.prototype
ます。
var b = new Bar;
// Get all Bar.prototype properties
console.log(b.__proto__ === Bar.prototype)
// Get all Object.prototype properties
console.log(b.__proto__.__proto__ === Object.prototype)
あなたがしようとFunction.prototype
しているすべてのものに代入をすると、プロトタイプチェーンが別のオブジェクトを含むように拡張されます。これは、単独でリンクされたリストに挿入するようなものです。
これは基本的に[[Prototype]]
連鎖を変更し、割り当てられたオブジェクトに定義されているプロパティがそのFunction.prototype
関数によって作成されたオブジェクトによって見えるようにします。
[1:それは誰を混乱させません。多くの実装では__proto__
プロパティを介して利用できるようになりました。
[2]:すべてを除くnull
。
このスレッドを読んだら、JavaScript Prototype Chainと混乱していて、これらのチャートが見つかりました
http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance
プロトタイプチェーンによるJavaScriptの継承を示す明確なグラフです
そして
http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/
これにはコードといくつかの素敵なダイアグラムの例が含まれています。
プロトタイプチェーンは最終的にObject.prototypeに落ちます。
プロトタイプチェーンは、サブクラスのプロトタイプを親クラスのオブジェクトに等しく設定することで、必要なときに技術的に延長することができます。
JavaScript Prototype Chainについて理解していただければ幸いです。
すべてのJavaScriptオブジェクトには、 [[Prototype]]と呼ばれる内部プロパティがあります。 obj.propName
またはobj['propName']
をobj.propName
してプロパティを検索し、そのオブジェクトにobj.hasOwnProperty('propName')
を介してチェックできるプロパティがない場合、ランタイムはオブジェクト内のプロパティを検索します代わりに[[Prototype]]によって参照されます。 プロトタイプオブジェクトにもそのようなプロパティがない場合は、そのプロトタイプが順番にチェックされ、一致が見つかるまで、またはその終わりに達するまで元のオブジェクトのプロトタイプチェーンを歩きます。
JavaScript実装の中には、[[Prototype]]プロパティへの直接アクセスを許可するものがあります(例: __proto__
という__proto__
標準プロパティを使用)。 一般に、オブジェクトの作成中にオブジェクトのプロトタイプを設定することは可能です。 new Func()
を使用して新しいオブジェクトを作成すると、オブジェクトの[[Prototype]]プロパティはFunc.prototype
によって参照されるオブジェクトに設定されます。
JavaScriptの継承システムは、これまでのようにプロトタイプであり、クラスベースではありませんが、これはJavaScriptでクラスをシミュレートすることを可能にします:
クラスとしてのコンストラクタ関数と、プロトタイプのプロパティ(つまり、コンストラクタ関数のprototype
プロパティによって参照されるオブジェクトのプロパティ)を、共有メンバ、つまり各インスタンスで同じメンバとして考えてください。 クラスベースのシステムでは、メソッドは各インスタンスに対して同じ方法で実装されるため、メソッドは通常プロトタイプに追加されますが、オブジェクトのフィールドはインスタンス固有であり、したがって構築中にオブジェクト自体に追加されます。
すべてのオブジェクトには[[Prototype]]という内部プロパティがあり、別のオブジェクトにリンクしています。
object [[Prototype]] -> anotherObject
伝統的なjavascriptでは、リンクされたオブジェクトは関数のprototype
プロパティです:
object [[Prototype]] -> aFunction.prototype
いくつかの環境では[[Prototype]]が__proto__
として公開されています:
anObject.__proto__ === anotherObject
オブジェクトの作成時に[[Prototype]]リンクを作成します。
// (1) Object.create:
var object = Object.create(anotherObject)
// object.__proto__ = anotherObject
// (2) ES6 object initializer:
var object = { __proto__: anotherObject };
// object.__proto__ = anotherObject
// (3) Traditional JavaScript:
var object = new aFunction;
// object.__proto__ = aFunction.prototype
したがって、これらのステートメントは同等です:
var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;
new
ステートメントは、リンクターゲット( Object.prototype
)自体を表示しません。 代わりにターゲットはコンストラクタ( Object
)によって暗示されます。
注意:
- すべてのオブジェクトにリンク[[Prototype]]があり、
__proto__
として公開されることもあります。 - すべての関数に
prototype
プロパティがあります。 -
new
作成されたオブジェクトは、そのコンストラクタのprototype
プロパティにリンクされます。 - 関数がコンストラクタとして使用されることがない場合、
prototype
プロパティは使用されなくなります。 - コンストラクタが必要ない場合は、
new
代わりにObject.createを使用します。
obj_n.prop_X
参照されているときに、「プロトタイプチェーン」を再帰的な規則として説明すると便利です。
obj_n.prop_X
存在しない場合はobj_n+1.prop_X
、obj_n+1 = obj_n.[[prototype]]
もし、prop_X
k番目のプロトタイプオブジェクトに最後に見つかった場合は
obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X
Javascriptオブジェクトの関係のグラフは、そのプロパティでここで見つけることができます:
prototypal
継承の概念は、多くの開発者にとって最も複雑なものの1つです。prototypal inheritance
より良い理解のために問題の根本を理解しようとしましょう。plain
関数から始めましょう。
のnew
上の演算子を使うと、Tree function
それをconstructor
関数と呼びます。
すべてのJavaScript
機能にはaがありprototype
ます。あなたがログオンするとTree.prototype
、あなたは...
上記のconsole.log()
出力を見ると、コンストラクタプロパティTree.prototype
と__proto__
プロパティも見ることができます。__proto__
表しprototype
、これはことをfunction
オフに基づいて、これは単なるですので、されてJavaScript function
いないとinheritance
まだ設定、それが参照するObject prototype
だけではJavaScriptに組み込まれて何かあります...
これには以下のようなものがあり.toString, .toValue, .hasOwnProperty
ます...
__proto__
私のmozillaを持ってきたものは非難されObject.getPrototypeOf
ていobject's prototype
ます。
Object.getPrototypeOf(Tree.prototype); // Object {}
私たちにメソッドを追加しましょうTree
prototype
。
私たちはそれを修正しRoot
、そのfunction
ブランチを追加しました。
つまり、を作成するinstance
とTree
、そのbranch
メソッドを呼び出すことができます。
我々はまた、追加することができますprimitives
またはobjects
当社へPrototype
。
child-tree
私たちを私たちに追加しましょうTree
。
ここではTreeからChild
継承しprototype
ています。ここでやってObject.create()
いることは、渡すものに基づいて新しいオブジェクトを作成するメソッドを使用していることですTree.prototype
。このケースでは、ChildのTree
プロトタイプを、プロトタイプと同じに見える新しいオブジェクトに設定しています。次に、私たちがChild's constructor to Child
それを指摘しなければ、それを設定していTree()
ます。
Child
今、自身の持っているprototype
、その__proto__
へのポイントTree
とTree's prototype
ベースにポイントをObject
。
Child
|
\
\
Tree.prototype
- branch
|
|
\
\
Object.prototype
-toString
-valueOf
-etc., etc.
これinstance
でChild
、branch
もともと利用可能なof とcall が作成されTree
ます。私たちは実際に私たちbranch
を定義していませんChild prototype
。しかし、Root prototype
子どもが継承しているところで。
JSではすべてがオブジェクトではなく、すべてがオブジェクトのように動作することができます。
Javascript
strings, number, booleans, undefined, null.
彼らはそうobject(ie reference types)
ではないが、確かに同じように行動することができるようなプリミティブを持っていobject
ます。ここで例を見てみましょう。
このリストの最初の行には、primitive
文字列値がnameに割り当てられています。2行目は、名前のように扱い、ドット表記を使ってobject
呼び出しますcharAt(0)
。
これは裏で起こることです:// JavaScript
エンジンは何をしますか
これString object
は破壊される前の1つのステートメントのみに存在します(プロセスが呼び出されますautoboxing
)。もう一度私たちに戻りましょうprototypal
inheritance
。
-
Javascript
にdelegation
基づいて継承をサポートしていprototypes
ます。 - それぞれに
Function
はprototype
別のオブジェクトを参照するプロパティがあります。 -
properties/functions
存在していない場合は、object
それ自体またはprototype
チェーンを介して見られます
prototype
JS の中のA は、yields
別のものの親にあなたが持っているオブジェクトですobject
。[すなわち、委任] Delegation
は、あなたが何かをすることができない場合、他の人にあなたのためにそれをするように指示します。
https://jsfiddle.net/say0tzpL/1/
上記のフィドルを調べると、犬はtoString
メソッドにアクセスできますが、そのメソッドでは利用できませんが、代理するプロトタイプチェーンを介して利用できますObject.prototype
あなたが下のものを見ると、私たちはcall
毎回利用可能な方法にアクセスしようとしていますfunction
。
https://jsfiddle.net/rknffckc/
上記のフィドルを調べると、Profile
Functionはcall
メソッドにアクセスできますが、そのメソッドでは使用できませんが、代理するプロトタイプチェーンを介して利用できますFunction.prototype
注: prototype
関数コンストラクターのプロパティですが、関数コンストラク__proto__
ターから構築されたオブジェクトのプロパティです。すべての関数にはprototype
値が空のプロパティが付属していますobject
。関数のインスタンスを作成するときは、内部プロパティを取得する[[Prototype]]
か__proto__
、その参照が関数のプロトタイプとなりconstructor
ます。
上の図はちょっと複雑に見えますが、prototype chaining
作品全体のイメージを引き出しています。これをゆっくりと歩みましょう:
2つのインスタンスがb1
ありb2
、そのコンストラクタはBar
parentであり、parentはFooであり、プロトタイプchain identify
とspeak
viaの2つのメソッドがBar
あります。Foo
https://jsfiddle.net/kbp7jr7n/
上記のコードを調べるとFoo
、メソッドを持つコンストラクタと、メソッドを持つコンストラクタが生成されます。2つのインスタンスを作成し、その親の型はです。ofのメソッドを呼び出している間、私たちはチェーンを介して話している人を特定することができます。identify()
Bar
speak
Bar
b1
b2
Foo
speak
Bar
prototype
Bar
今ではすべてのメソッドFoo
が定義されていprototype
ます。のは理解する上で、さらに掘り下げてみようObject.prototype
とFunction.prototype
し、それらがどのように関連しています。あなたはのコンストラクタを見ればFoo
、Bar
とObject
していますFunction constructor
。
IS 、のであるあなたが密接に見ればとのに関係しています。prototype
Bar
Foo
prototype
Foo
Object
prototype
Foo
Object.prototype
これを閉じる前に、上のすべてを要約するために小さなコードをここに入れてみましょう。instanceof
ここで演算子を使用object
してprototype
チェーンの中prototype
にa のプロパティがあるかどうかを確認してconstructor
います。
私はこのアドの情報がほしいと思っていますが、私はこれを理解するのに大きな可能性があることを知っています...単純な言葉で、それはオブジェクトにリンクされたオブジェクトです!
このタイプのものを理解するには、私はいつも類推が好きです。プロトタイプははるかに単純なパラダイムですが、 'Prototypical inheritance'は私の意見ではクラスベースの継承と比べてかなり混乱しています。実際にプロトタイプでは、実際には継承がないので、それ自体が誤解を招くような名前であり、より一種の「委任」です。
これを想像して....
あなたは高校に通っていて、授業中で今日はクイズがありますが、答えを記入するペンはありません。ドー!
あなたはペンを持っている可能性があるあなたの友人のFinniusの隣に座っています。あなたは尋ねると、彼は自分の机の周りをうまく見ることができませんが、 "私はペンがありません"と言っているのではなく、彼はペンがあれば他の友人Derpとチェックする素敵な友人です。Derpは本当に余分なペンを持っていて、それをFinniusに戻します。Finniusはあなたのクイズを完了するためにそれを渡します。Derpはペンを使用するためにあなたにペンを委任したFinniusにペンを委ねました。
ここで重要なのは、あなたが彼と直接関係がないので、Derpはあなたにペンを与えないということです。
これは、プロトタイプがどのように機能するかの簡単な例です。探しているものについてデータツリーが検索されます。
より良い画像でJavaScriptプロトタイプベースの継承を説明する別の試み
プロトタイプについての私の理解を教えてください。私はここで他の言語と継承を比較するつもりはありません。私は人々が言語の比較をやめ、その言語自体を理解することを望む。プロトタイプとプロトタイプの継承を理解することはとても簡単です。
プロトタイプは、製品を作成するためのモデルのようなものです。理解しておくべき重要なポイントは、プロトタイプとして別のオブジェクトを使用してオブジェクトを作成すると、プロトタイプと製品の間のリンクが永続的であることです。例えば:
var model = {x:2};
var product = Object.create(model);
model.y = 5;
product.y
=>5
すべてのオブジェクトには[[prototype]]と呼ばれる内部プロパティが含まれていObject.getPrototypeOf()
ます。このプロパティは関数によってアクセスできます。Object.create(model)
新しいオブジェクトを作成し、そのオブジェクトの[[prototype]]プロパティをオブジェクトモデルに設定します。したがって、あなたが行うとObject.getPrototypeOf(product)
、あなたはオブジェクトモデルを取得します。
製品のプロパティは次のように処理されます。
- 値を読み取るためにプロパティにアクセスすると、スコープチェーンで参照されます。変数の検索は、プロダクトからプロトタイプにかけて始まります。そのような変数が検索で見つかった場合は、すぐに検索が停止され、値が返されます。そのような変数がスコープチェーン内に見つからない場合は、undefinedが返されます。
- プロパティーが書き込まれる(変更される)と、そのプロパティーは常に製品オブジェクトに書き込まれます。製品がすでにこのようなプロパティーを持っていない場合、暗黙的に作成され、書き込まれます。
プロトタイププロパティを使用するオブジェクトのそのようなリンクは、プロトタイプ継承と呼ばれます。そこに、それはとても簡単です、同意しますか?
次のkeyValueStore
オブジェクトを考えてみましょう。
var keyValueStore = (function() {
var count = 0;
var kvs = function() {
count++;
this.data = {};
this.get = function(key) { return this.data[key]; };
this.set = function(key, value) { this.data[key] = value; };
this.delete = function(key) { delete this.data[key]; };
this.getLength = function() {
var l = 0;
for (p in this.data) l++;
return l;
}
};
return { // Singleton public properties
'create' : function() { return new kvs(); },
'count' : function() { return count; }
};
})();
次のようにして、このオブジェクトの新しいインスタンスを作成できます。
kvs = keyValueStore.create();
このオブジェクトの各インスタンスは、次のパブリックプロパティを持ちます。
-
data
-
get
-
set
-
delete
-
getLength
ここで、このkeyValueStore
オブジェクトのインスタンスを100個作成するとします。にもかかわらずget
、set
、delete
、getLength
これらの100個のインスタンスごとに、まったく同じことを行いますが、すべてのインスタンスは、この関数の独自のコピーを持っています。
さて、あなたは1つだけ持っていることができれば想像しget
、set
、delete
およびgetLength
コピーを、各インスタンスは、同じ機能を参照します。これは、パフォーマンスが向上し、メモリが少なくて済みます。
プロトタイプは継承されるがインスタンスによってコピーされないプロパティの「青写真」である。これは、オブジェクトのすべてのインスタンスに対して1回だけメモリ内に存在し、それらのインスタンスすべてで共有されることを意味します。
さて、keyValueStore
オブジェクトをもう一度考えてみましょう。私は次のように書き直すことができます:
var keyValueStore = (function() {
var count = 0;
var kvs = function() {
count++;
this.data = {};
};
kvs.prototype = {
'get' : function(key) { return this.data[key]; },
'set' : function(key, value) { this.data[key] = value; },
'delete' : function(key) { delete this.data[key]; },
'getLength' : function() {
var l = 0;
for (p in this.data) l++;
return l;
}
};
return {
'create' : function() { return new kvs(); },
'count' : function() { return count; }
};
})();
これは、keyValueStore
オブジェクトの以前のバージョンとまったく同じですが、すべてのメソッドが現在プロトタイプに入れられています。これが意味することは、100インスタンスのすべてが、それぞれが独自のコピーを持つ代わりに、これらの4つのメソッドを共有することです。