何が起こっているかは、いくつかの例で非常に簡単に説明できます。
プロトタイプチェーンを介したpersonal_detailsへのアクセス
var person = { name :"dummy", personal_details: { age : 22, country : "USA" } }
var bob = Object.create(person)
bob.name = "bob"
bob.personal_details.age = 23
次のような出力:
console.log( bob );
/// { name :"bob", personal_details: { age : 23, country : "USA" } }
console.log( person )
/// { name :"dummy", personal_details: { age : 23, country : "USA" } }
23歳がpersonオブジェクトに設定され、bobのプロトタイプチェーンを介しbecause bob.personal_details
て直接参照されます。person.personal_details
オブジェクト構造を下に移動すると、オブジェクトを直接操作しperson.personal_details
ます。
プロトタイプ化されたプロパティをローカルプロパティでオーバーライドする
ただし、bobのpersonal_details
プロパティを別のオブジェクトでオーバーライドすると、そのプロトタイプリンクはよりローカルなプロパティによってオーバーライドされます。
bob.personal_details = { a: 123 }
これで、出力は次のようになります。
console.log( bob );
/// { name :"bob", personal_details: { a : 123 } }
console.log( person )
/// { name :"dummy", personal_details: { age : 23, country : "USA" } }
bob.personal_details
したがって、これからアクセスすることで、人{ a: 123 }
の元の{ age : 23, country : "USA" }
オブジェクトではなく、オブジェクトを参照していることになります。bob
加えられたすべての変更はそのオブジェクトで発生し、基本的にはオブジェクトとは関係ありませんperson
。
プロトタイプチェーン
物事を面白くするために、これを行うと、上記のすべての後に何が起こると思いますか?
delete bob.personal_details
person.personal_details
(追加したローカルプロパティを削除したため)への元のプロトタイプリンクを復活させることになり、コンソールログに次のように表示されます。
console.log( bob );
/// { name :"bob", personal_details: { age : 23, country : "USA" } }
基本的に、JavaScriptエンジンは、各プロトタイプオブジェクトで要求しているプロパティまたはメソッドが見つかるまで、プロトタイプチェーンに沿って戻ります。アイテムがチェーンのさらに上に設定されると、後で他のアイテムをオーバーライドすることを意味します。
さて、別の質問ですが、次のことをもう一度実行するとどうなりますか?
delete bob.personal_details
何もありません。bobに割り当てられた実際のプロパティはもうありませんpersonal_details
。delete
現在のオブジェクトでのみ機能し、プロトタイプチェーンをたどることはありません。
別の見方
プロトタイプチェーンがどのように機能するかを確認する別の方法は、基本的にオブジェクトのスタックを想像することです。JavaScriptが特定のプロパティまたはメソッドをスキャンすると、次の構造を介して下向きに読み取られます。
bob : { }
person : { name: 'dummy', personal_details: { age: 22 } }
Object : { toString: function(){ return '[Object object]'; } }
例として、にアクセスしたいとしますbob.toString
。は、ほぼすべての基本プロトタイプであるtoString
JavaScriptの基本オブジェクトに存在するメソッドです。Object
インタプリタがオブジェクトの特定のメソッドまたはプロパティの読み取り要求を受け取ると、次の一連のイベントに従います。
bob
と呼ばれるプロパティがありますtoString
か?いいえ。
bob.__proto__
つまりperson
、というプロパティがありますtoString
か?いいえ。
bob.__proto__.__proto__
つまりObject
、というプロパティがありますtoString
か?はい
- 参照を返す
function(){ return '[Object object]'; }
ポイント4に達すると、インタプリタはtoString
Objectで見つかったメソッドへの参照を返します。未定義Object
のプロパティのエラーでプロパティが見つからなかった場合は、おそらく発生している可能性があります(チェーンの最後であるため)。
さて、前の例を見て、今度はtoString
メソッドを定義するとしますbob
-そう:
bob : { toString: function(){ return '[Bob]'; } }
person : { name: 'dummy', personal_details: { age: 22 } }
Object : { toString: function(){ return '[Object object]'; } }
もう一度bobのメソッドを読み取りアクセスしようとするとtoString
、今度は次のようになります。
bob
と呼ばれるプロパティがありますtoString
か?はい。
プロセスは最初のハードルで停止し、toString
bobからメソッドを返します。これは、ではなくbob.toString()
戻ることを意味します。[Bob]
[Object object]
Phant0mによって簡潔に述べられているように、オブジェクトへの書き込み要求は別のパスをたどり、プロトタイプチェーンをたどることはありません。これを理解することは、読み取りと書き込み要求の違いを理解することです。
bob.toString --- is a read request
bob.toString = function(){} --- is a write request
bob.personal_details --- is a read request
bob.personal_details = {} --- is a write request
bob.personal_details.age = 123 --- is a read request, then a write request.
最後の項目は混乱を引き起こしている項目です。そして、プロセスは次のルートに従います。
bob
と呼ばれるプロパティがありますpersonal_details
か?いいえ。
person
と呼ばれるプロパティがありますpersonal_details
か?はい。
{ age: 22 }
メモリ内のどこかにフローティングで保存されている参照を返します。
オブジェクトのナビゲーションまたは割り当ての各部分がプロパティまたはメソッドの新しいリクエストであるため、新しいプロセスが開始されます。これで、personal_details
オブジェクトを書き込み要求に切り替えることができます。これは、左側のプロパティまたは変数が=
等しい側を持っていたため、常に割り当てであるためです。
- オブジェクト
123
のプロパティに値を書き込みますage
{ age: 22 }
したがって、元のリクエストは次のように表示されます。
(bob.personal_details) --- read
(personal_details.age = 123) --- write
bob
プロセスの独自のプロパティを所有していた場合personal_details
は同じですが、書き込まれるターゲットオブジェクトは異なります。
そして最後に...
あなたの質問の線に沿って入れてください:
プロトタイプオブジェクトのプロパティがREAD_ONLYとして扱われることを理解するのは困難ですが、プロパティがオブジェクトである場合は、そのプロパティを取得して、そのプロパティを自由に変更できます。私の理解は正しいですか?
プロトタイプ化されたプロパティは一見読み取り専用ですが、それらを継承しているオブジェクトのプロパティとして直接アクセスされた場合にのみ、これらのプロパティは実際には継承オブジェクトにまったく存在しないためです。プロトタイプオブジェクト自体に移動すると、それがまさに通常のオブジェクトであるため、通常のオブジェクト(読み取りと書き込みを含む)と同じように扱うことができます。最初は混乱するかもしれませんが、これは性質またはプロトタイプの継承であり、作業しているプロパティにアクセスする方法がすべてです。