[英]Javascript Prototypal Inheritance & object properties shadowing
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.personal_details === person.personal_details);
// true : since it does not shadow object of prototype object
console.log(bob.name === person.name);
// false : since it shadows name
////now
bob.personal_details = {a:1};
console.log(bob.personal_details === person.personal_details);
//假
當對象Bob試圖重寫的人 “name”屬性的話,它本身鮑勃被遮蔽。
但是在personal_details的情況下,將違反相同規則。
我很想知道為什么會這樣?
這是jsbin的鏈接: http ://jsbin.com/asuzev/1/edit
下面的例子可以很容易地說明發生了什么:
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" } }
現在在person對象上設置了23歲, because bob.personal_details
是通過bob的原型鏈直接引用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
上的方法,該Object
是幾乎所有內容的基礎原型。 當解釋器獲得對對象上特定方法或屬性的讀取請求時,它將遵循以下事件鏈:
bob
是否有一個名為toString
的屬性? 沒有。 bob.__proto__
即person
是否具有稱為toString
的屬性? 沒有。 bob.__proto__.__proto__
即Object
是否具有稱為toString
的屬性? 是 function(){ return '[Object object]'; }
的引用function(){ return '[Object object]'; }
function(){ return '[Object object]'; }
一旦到達點4,解釋器將返回對Object上toString
方法的引用。 如果未在Object
上找到該屬性,則很可能引發了未定義屬性的錯誤(因為它是鏈中的最后一個)。
現在,如果我們以前面的示例為例,這次在bob
上定義一個toString
方法-因此:
bob : { toString: function(){ return '[Bob]'; } }
person : { name: 'dummy', personal_details: { age: 22 } }
Object : { toString: function(){ return '[Object object]'; } }
如果我們嘗試再次讀取bob的toString
方法,這一次我們得到:
bob
是否有一個名為toString
的屬性? 是。 該過程在第一個障礙處停止,並從bob返回toString
方法。 這意味着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: 22 }
的屬性age
因此原始請求可以看作是這樣的:
(bob.personal_details) --- read
(personal_details.age = 123) --- write
如果bob
擁有自己的personal_details
財產,則過程將相同,但寫入的目標對象將不同。
提出您的問題:
很難理解原型對象上的屬性被視為READ_ONLY,但是如果屬性是一個對象,那么人們就可以擁有它並可以自由地修改其屬性! 我的理解正確嗎?
原型屬性看似只讀,但僅當作為繼承它們的對象的屬性直接訪問時才使用-因為這些屬性實際上根本不在繼承對象上存在。 如果您向下瀏覽到原型對象本身,則可以將其視為任何普通對象(具有讀寫功能),因為它就是所謂的普通對象。 最初可能會造成混淆,但這是性質或原型繼承,而這完全取決於您如何訪問要使用的屬性。
在第二種情況下,您沒有分配給bob
屬性,那么如何覆蓋它呢?
如果bob.name = "bob"
,則綁定bob 自己的 name屬性。
在第二種情況下,您沒有 。 您可以通過原型訪問bob的personal_details
屬性。 然后,您將其分配給該對象的屬性,到那時bob
所有連接都將丟失。
這樣想:
bob.personal_details.age = 23;
// equivalent
expr = bob.personal_details;
expr.age = 23; // As you can see, there's no assignment to bob
它不違反任何規則,因為情況完全不同。
我希望下面的圖表足夠清楚,但是我將嘗試簡要解釋發生了什么。
使用Object.create
創建新對象時,將使用原型create
方法的第一個參數創建對象。 因此,您將創建帶有原型的bob
,該原型指向person
對象。 通過引用bob
的原型,可以訪問person
所有屬性。
在下一張圖片中,您更改bob
的名稱。 現在是bob
因此只需創建名稱為name
的bob
新slot
或property
(現在解釋器在查找屬性name
時不會檢查原型鏈,它將直接發現bob具有此類屬性)。
在第三個中,您更改了影響到person.personal_details.age
bob.personal_details.age
,因為這是同一對象。
最后,您設置了屬性personal_details
,現在bob
具有slot personal_details
,它不是原型屬性,而是對另一個對象(匿名對象)的引用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.