継承と原型
6225 ワード
私たちはJavaScriptという言語を検討していますが、いつも回り道をしている話題の一つは「継承とプロトタイプの鎖」です.では、「継承とプロトタイプの鎖」とは一体何ですか?
私はとても好きなチャットモードは:XXは何ですか?私はXXのようなものしか言えません.つまり、私は直接あなたに定義を言いません.通常は「定義」という概念が難しく、例えば「クローズド」の定義について――クローズドは関数とその関数を宣言する語法環境の組み合わせです.
まず、JavaScriptでは、どのように「継承と原型チェーン」が表現されているのかを見てみましょう.
“継承とプロトタイプチェーン”は何ですか?
Javaなどの静止語と違って、JavaScriptという言語には「類」という概念がありません.すべての継承は原型に基づいています.まず直接例を見ます.
上記の栗の中では、対象
でも待ってください.
どの原型からも受け継がれていないオブジェクトを作成してもいいですか?答えは大丈夫です.
JavaScriptは私たちに
これで、私達も
“継承とプロトタイプチェーン”は何ですか?
ここまで読んで、私たちはもう相続と原型チェーンを定義できると信じています.
原型チェーン
プロトタイプチェーンとは、オブジェクトの
この例では、
new演算子
前の紙面の中で、JavaScriptの中で、対象は字面の量の形式と
私たちは任意の関数に対して
コンストラクタは自分のプロトタイプのオブジェクトを持っていますから、もう一つのコンストラクタを彼のプロトタイプのオブジェクトを継承してもいいですか?
しかし、これは完全に継承されますか?継承にはプロトタイプが必要ですが、jackのプロトタイプチェーンには
ES 6のクラス
実は前のセクションは難しく見えます.ES 6の前にはJavaScriptには類の概念がないからです.(もちろんその後もないです.)しかし、私達は「構造関数」があります.前のセクションの栗は構造関数の「Male」と言って、コンストラクションの「Human」を継承しました.
その時はちょっと気まずい場面だったと覚えていますが、みんなは手をこすってうつむいています.何を言っているのか分かりませんでした.
幸いES 6には
おわりに
実は全編の核心はやはりあの話です.JavaScriptの継承は原型に基づいています.多くの内容を説明してくれませんでした.
参照継承と原型
私はとても好きなチャットモードは:XXは何ですか?私はXXのようなものしか言えません.つまり、私は直接あなたに定義を言いません.通常は「定義」という概念が難しく、例えば「クローズド」の定義について――クローズドは関数とその関数を宣言する語法環境の組み合わせです.
まず、JavaScriptでは、どのように「継承と原型チェーン」が表現されているのかを見てみましょう.
“継承とプロトタイプチェーン”は何ですか?
Javaなどの静止語と違って、JavaScriptという言語には「類」という概念がありません.すべての継承は原型に基づいています.まず直接例を見ます.
var obj = {
a: 0,
f: function() {
return this.a + 1
}
}
var obj_1 = {} // cat sound speak
obj_1.__proto__ = obj
console.log(obj_1.a) // 0
console.log(obj_1.f()) // 1
上記のように、obj_1
というオブジェクトを定義したときには、a
とf
の属性と方法を宣言していませんでしたが、それでもそれらを見つけることができます.これはJavaScriptの中で、ある属性(JavaScriptの対象はすべてキーの値が正しい形式です.だから、方法は属性も計算できます.)を対象に探しています.彼はまずその対象地で探しています.ない場合、彼はプロトタイプのチェーンに沿って上の層に探しています.上記の栗の中では、対象
obj_1
は当地では何の属性も定義されていませんので、obj_1.a
を実行すると、プロトタイプのチェーンに沿って上を探します.obj_1.__proto__ = obj
では、obj
をobj_1
の__proto__
の属性に与えた.でも待ってください.
__proto__
は何ですか?__proto__
属性が指しているのはobj_1
のプロトタイプです.obj
のプロトタイプは何ですか?obj.__proto__
をプリントしてみてもいいです.結果としては、たくさんのものがプリントされています.これらは実はObject.prototype
です.つまり「究極の原型」です.このオブジェクトはもう原型を引き継ぎません.前に述べたように、obj_1
もこの上の属性に直接アクセスできるはずです.事実も確かです.例えば:obj_1.hasOwnProperty('a') // false
obj_1
でhasOwnProperty
方法を定義していませんが、それでもこの方法を見つけることができます.実際には、オブジェクトの字面量(Object Literal)として作成されたすべてのオブジェクトは、Object.prototype
上のすべての属性を継承しています.どの原型からも受け継がれていないオブジェクトを作成してもいいですか?答えは大丈夫です.
JavaScriptは私たちに
Object.create
という方法を提供してくれました.これを通じて、特定の対象となるプロトタイプを作成することができます.nullが伝わったら、「原型は空」の対象を作ることができます.var a = Object.create(null)
この例では、a
は空のオブジェクトとなり、ローカルには属性がないだけでなく、プロトタイプチェーンもない、つまりObject.prototype
を継承していない.(考えてみます.このような空の対象はいったい何の役割がありますか?)これで、私達も
Object.create
を利用して相続を実現できますよね?はい、そうですvar obj = {
a: 0,
f: function() {
return this.a + 1
}
}
var obj_2 = Object.create(obj)
console.log(obj_2.a) // 0
console.log(obj_2.f()) // 1
しかし改めて想像してみると、継承の本質は何ですか?原型を継ぐ!どんな方法でも大丈夫です.私のプロトタイプのチェーンにあなたを見つけられたらいいです.obj
に属性a
が定義されていますが、もし私がobj_2
にもう一つの属性a
を定義したら、プリントされたのは誰のa
ですか?var obj = {
a: 0,
f: function() {
return this.a + 1
}
}
var obj_2 = Object.create(obj)
obj_2.a = 2
console.log(obj_2.a) // 2
答えは明らかです.属性を探すときは、常に現在のオブジェクトから始まります.現在のオブジェクトにこの属性が見つかったら、クエリは停止されます.したがって、プロトタイプのチェーンが長すぎると、前のプロトタイプの属性を探すときに時間がかかります.このような長すぎるプロトタイプチェーンはできるだけ避けるべきです.“継承とプロトタイプチェーン”は何ですか?
ここまで読んで、私たちはもう相続と原型チェーンを定義できると信じています.
原型チェーン
プロトタイプチェーンとは、オブジェクトの
__proto__
から始まり、この線の最終端まで、ほとんどの場合、この最終端はObject.prototype
である.例えば上記の例:var obj = {
a: 0,
f: function() {
return this.a + 1
}
}
var obj_2 = Object.create(obj)
// obj_2.__proto__ === obj
// obj.__proto__ === Object.prototype
引き継ぐこの例では、
obj --- Object.prototype
はプロトタイプチェーンを構成し、プロトタイプチェーンに沿って、どのオブジェクトから最初に継承されたかを見つけることができ、同時に、プロトタイプチェーン上の各ノードは上流のオブジェクトのすべての属性を継承することができる.説明を引き継ぐのは、関係または動作であるべきです.new演算子
前の紙面の中で、JavaScriptの中で、対象は字面の量の形式と
Object.create
の形式で創建することができます.しかし、JavaScriptにはもう一つの方法があります.new
演算子を使ってオブジェクトを作成します.var obj = new Object
console.log(obj) // {}
前の内容からobj
がObject.prototype
オブジェクトの属性を継承していることが分かります.new
オペレータについては、他のコラムを見てもいいです.JavaScriptの中でnewのオブジェクトがある時、私たちは一体何をしていますか?じゃObject
は何ですか?typeof Object
を実行します.プリントしたのは「function」です.はい、Object
は関数です.正確には、構造関数です.new
演算子は、関数であるべきです.私たちは任意の関数に対して
new
動作を実行できます.しかし、関数がコンストラクターとして使用され、オブジェクトを具体化する場合、イニシャルを大文字にする傾向があります.var Foo = function(x) {
this.x = x
}
var boo = new Foo(1)
console.log(boo, boo.x) // Foo {x: 1} 1
コンストラクタは,オブジェクトを初期化し,コンストラクタにおいて,いくつかの初期化動作を行うことができます.通常、JavaScriptプラグインを作成する時、全体のオブジェクトに構造関数をマウントします.この構造関数を実際化することにより、原型オブジェクトのすべての属性を継承できます.コンストラクタは自分のプロトタイプのオブジェクトを持っていますから、もう一つのコンストラクタを彼のプロトタイプのオブジェクトを継承してもいいですか?
var Human = function(name) {
this.name = name
}
var Male = function(name) {
Human.call(this, name)
this.gender = 'male'
}
var jack = new Male('jack')
console.log(jack) // Male {name: "jack", gender: "male"}
構造関数内部でHuman
関数を実行し、Human
関数内部のthis
ポインタを変更しました.同時に、this
のプロトタイプに自分の属性Male
を定義し、このようにして、実用化されたオブジェクトは同時に二つの属性を持つ.しかし、これは完全に継承されますか?継承にはプロトタイプが必要ですが、jackのプロトタイプチェーンには
gender
がありません.私たちは追加の2ステップが必要です.var Human = function(name) {
this.name = name
}
var Male = function(name) {
Human.call(this, name)
this.gender = 'male'
}
Male.prototype = Object.create(Human.prototype)
Male.prototype.constructor = Male
var jack = new Male('jack')
console.log(jack) // Male {name: "jack", gender: "male"}
そうしたら、jackのプロトタイプチェーンにHuman
が見つかります.ES 6のクラス
実は前のセクションは難しく見えます.ES 6の前にはJavaScriptには類の概念がないからです.(もちろんその後もないです.)しかし、私達は「構造関数」があります.前のセクションの栗は構造関数の「Male」と言って、コンストラクションの「Human」を継承しました.
その時はちょっと気まずい場面だったと覚えていますが、みんなは手をこすってうつむいています.何を言っているのか分かりませんでした.
幸いES 6には
Human
のキーワードがあります.これは文法飴です.本質的に、JavaScriptの継承はまだ原型に基づいています.でも、少なくとも形式的には、クラスによってコードを書くことができます.class Human {
constructor(name) {
this.name = name
}
}
class Male extends Human {
constructor(name) {
super(name)
this.gender = 'male'
}
}
var jack = new Male('jack')
console.log(jack) // Male {name: "jack", gender: "male"}
コンソールの上でClass
の層に沿って下にひっくり返ります.__proto__
とclass Male
を見つけられます.これは私達の継承が成功したということです.また、JavaScriptにはこのようなものがありませんでしたが、「類Male継承クラスHuman」とも解釈できます.おわりに
実は全編の核心はやはりあの話です.JavaScriptの継承は原型に基づいています.多くの内容を説明してくれませんでした.
参照