javascript原型模式容易忽略的地方
原型模式+構造函數應該是javascript編輯時使用的非常多的功能。
例如:
function Tang(name,age){
this.name=name;
this.age=age;
this.init();
}
Tang.prototype={
init:function(){
},
alertName:function(){
alert(this.name);
}
}
上面這個原型Tang.prototype={}就是字面量的表示方法。
注意當我們使用字面量表示時,本質上完全重寫了默認的prototype對象,因此constructor屬性也就變成了新的constructor屬性(指向Object構造函數),不在指向Tang函數。
如果要讓constructor指向Tang函數,可以強制指向:
Tang.prototype={
constructor:Tang,//強制執行Tang
init:function(){
},
alertName:function(){
alert(this.name);
}
}
字面量方式爲什麼constructor會指向Object?
因爲Tang.protorype={},這種寫法其實就是創建了一個新對象,而每創建一個函數,就會同時創建它的prototype,這個對象也會自動獲取constructor屬性。所有,新對象的constructor重寫了Tang原來的constructor,因此會指向新對象,那個新對象沒有指向構造函數,那麼就會默認爲Object.
理解原型:
只要創建一個新函數,就會根據一組特定的規則爲該函數創建一個prototype屬性。在默認的情況下,所有prototype屬性都會自動獲得一個constructor(構造函數)屬性,這個屬性包含一個指向prototype屬性所在函數的指針。
當用構造函數創建一個新實例後,該實例的內部將包含一個指針,指向構造函數的原型屬性。
這個連接存在於實例與構造函數的原型屬性之間,而不是存在於實例和構造函數之間。換句話說,他們的內部屬性和構造函數沒有直接關係,該屬性僅僅指向了prototype。
當我們需要給原型添加屬性時,需要使用如下方法:
Tang.prototype.sayHi=function(){
alert("hi");
}
而不能用字面量形式:
下面一個例子:
function Tang(){
}
Tang.prototype={
constructor:Tang,
name:"guang"
}
var tang=new Tang();
Tang.prototype={
constructor:Tang,
name:"tang"
}
alert(tang.name);//guang
這裏要注意var tang=new Tang();的位置,在申明以後修改的原型是不起作用的
我們稍微修改下位置:
function Tang(){
}
Tang.prototype={
constructor:Tang,
name:"guang"
}
Tang.prototype={
constructor:Tang,
name:"tang"
}
var tang=new Tang();
alert(tang.name);//tang
在申明以前修改的原型就有效果了,可以通俗的理解爲,申明完之後,字面量形式就無法修改原型屬性了
我們先創建了Tang的實例,然後重寫了原型對象,然後調用Tang.name()發生了錯誤,因爲tang指向的原型中不包含以該名字命名的屬性。重寫原型對象切斷了現有原型與任何之前已經存在的對象實例之間的聯繫,他們引用的仍然是最初的原型。
原因:調用構造函數時會爲實例添加一個指向最初原型的_proto_指針,而把原型修改爲另外一個對象就等於切斷了構造函數與最初原型之間的聯繫,實例中的指針僅指向原型,而不指向構造函數。
當代碼讀取對象的某個屬性時,都會執行一次搜索,搜索首先從對象實例本身開始,如果在實例中找到了具有給定名字的屬性,就返回,如果沒有就搜索對象原型裏面的屬性。
可以通過對象實例訪問保持在原型中的值,但是不能通過對象實例重寫原型中的值。
爲對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性,使用delete操作符可以完全刪除實例屬性。
還有一個有點麻煩的問題:
上面我們能看的一句話:可以通過對象實例訪問保持在原型中的值,但卻不能通過對象實例重寫原型中的值。
然後我們會在瞭解原型中遇到一個問題,就是共享的問題,也就是a實例修改了,也會影響b實例
那麼上面兩句話是否矛盾呢,到底原型中的內容能不能被修改了?
答案是上面兩句話都對,原型中的基本值屬性不能被修改,而對於引用類型值的屬性則是可以修改了