什么是原型别智?
原型是Javascript中的繼承的繼承,JavaScript的繼承就是基于原型的繼承。
函數(shù)的原型對象
在JavaScript中,我們創(chuàng)建一個函數(shù)A(就是聲明一個函數(shù)), 那么瀏覽器就會在內(nèi)存中創(chuàng)建一個對象B,而且每個函數(shù)都默認會有一個屬性 prototype 指向了這個對象( 即:prototype的屬性的值是這個對象 )唁情。這個對象B就是函數(shù)A的原型對象疑苔,簡稱函數(shù)的原型。這個原型對象B 默認會有一個屬性 constructor 指向了這個函數(shù)A ( 意思就是說:constructor屬性的值是函數(shù)A )甸鸟。
<script>
function Person() {
Person.prototype.name="李五";
Person.prototype.eat=function () {
}
}
var p1=new Person()
var p2=new Person()
console.log(p1.name)
console.log(p2.name)//輸出的都是李五
//p1.name="張三" //只能改變它本身的值不能修改原型對象共享的值
//console.log(p1.name) 輸出的是張三
//可通過_proto_修改原型對象中的值 因為
//console.log(Person.prototype===p1._proto_)
//console.log(Person.prototype.constructor===Person)
p2.__proto__.name="小吳";//修改了對象中的值 后邊輸出的都是小吳
console.log(p1.name)
console.log(p2.name)
</script>
說明:
- 從上面的圖示中可以看到惦费,創(chuàng)建p1對象雖然使用的是Person構(gòu)造函數(shù),但是對象創(chuàng)建出來之后抢韭,這個p1對象其實已經(jīng)與Person構(gòu)造函數(shù)沒有任何關(guān)系了薪贫,p1對象的[[ proto ]]屬性指向的是Person構(gòu)造函數(shù)的原型對象。
- 如果使用new Person()創(chuàng)建多個對象刻恭,則多個對象都會同時指向Person構(gòu)造函數(shù)的原型對象瞧省。
- 我們可以手動給這個原型對象添加屬性和方法,那么p1,p2,p3...這些對象就會共享這些在原型中添加的屬性和方法鳍贾。
- 如果我們訪問p1中的一個屬性name鞍匾,如果在p1對象中找到,則直接返回骑科。如果p1對象中沒有找到橡淑,則直接去p1對象的[[proto]]屬性指向的原型對象中查找,如果查找到則返回咆爽。(如果原型中也沒有找到梁棠,則繼續(xù)向上找原型的原型---原型鏈。 后面再講)斗埂。
- 如果通過p1對象添加了一個屬性name符糊,則對p1對象來說就屏蔽了原型中的屬性name。 換句話說:在p1中就沒有辦法訪問到原型的屬性name了呛凶。
- 通過p1對象只能讀取原型中的屬性name的值濒蒋,而不能修改原型中的屬性name的值。 p1.name = "李四"; 并不是修改了原型中的值把兔,而是在p1對象中給添加了一個屬性name沪伙。
與原型有關(guān)的幾個屬性和方法
prototype屬性
prototype 存在于構(gòu)造函數(shù)中 (其實任意函數(shù)中都有,只不過不是構(gòu)造函數(shù)的時候prototype我們不關(guān)注而已) 县好,他指向了這個構(gòu)造函數(shù)的原型對象围橡。
constructor屬性
constructor屬性存在于原型對象中,他指向了構(gòu)造函數(shù)
proto 屬性(注意:左右各是2個下劃線)
用構(gòu)造方法創(chuàng)建一個新的對象之后缕贡,這個對象中默認會有一個不可訪問的屬性 [[proto]] , 這個屬性就指向了構(gòu)造方法的原型對象翁授。
hasOwnProperty() 方法
hasOwnProperty方法拣播,可以判斷一個屬性是否來自對象本身。
hasOwnProperty這個方法可以判斷一個屬性是否在對象本身添加的收擦,但是不能判斷是否存在于原型中贮配,因為有可能這個屬性不存在。
in 操作符
in操作符用來判斷一個屬性是否存在于這個對象中塞赂。但是在查找這個屬性時候泪勒,先在對象本身中找,如果對象找不到再去原型中找宴猾。換句話說圆存,只要對象和原型中有一個地方存在這個屬性,就返回true
組合使用原型模型和構(gòu)造函數(shù)模型創(chuàng)建對象
原型模型創(chuàng)建對象的缺陷
原型中的所有的屬性都是共享的仇哆。也就是說沦辙,用同一個構(gòu)造函數(shù)創(chuàng)建的對象去訪問原型中的屬性的時候,大家都是訪問的同一個對象讹剔,如果一個對象對原型的屬性進行了修改油讯,則會反映到所有的對象上面。
使用構(gòu)造函數(shù)模型創(chuàng)建對象的缺陷
在構(gòu)造函數(shù)中添加的屬性和方法延欠,每個對象都有自己獨有的一份撞羽,大家不會共享。這個特性對屬性比較合適衫冻,但是對方法又不太合適诀紊。
使用組合模式解決上述兩種缺陷
原型模式適合封裝方法,構(gòu)造方法模式適合封裝屬性隅俘,綜合兩種模式的優(yōu)點就有了組合模式邻奠。
<script>
function Person(name,age) {
this.name=name;
this.age=age;
//屬性不共享
}
Person.prototype.eat=function () {
console.log("玩"+this.name)
}//方法共享
Person.prototype.speak=function () {
console.log("是"+this.age)
}
var p1=new Person("張三",21)
var p2=new Person("王五",32)
console.log(p1.name)
p1.eat()
console.log(p2.name)
p1.speak()
</script>
動態(tài)原型模式創(chuàng)建對象
動態(tài)原型模式把所有的屬性和方法都封裝在構(gòu)造方法中,而僅僅在需要的時候才去在構(gòu)造方法中初始化原型为居,又保持了同時使用構(gòu)造函數(shù)和原型的優(yōu)點碌宴。
<script>
function Person(name,age) {
this.name=name;
this.age=age;
if(!Person.prototype.eat){
Person.prototype.eat=function () {
console.log("吃"+this.name)
}
}
if(!Person.prototype.speak){
Person.prototype.speak=function () {
console.log("玩"+this.age)
}
}
}
var p1=new Person("a",20)
var p2=new Person("b",30)
console.log(p1.speak==p2.speak)//返回true
</script>
說明:
- 構(gòu)造函數(shù)和普通函數(shù)僅僅也僅僅是調(diào)用方式的不同。也就是說蒙畴,隨便一個函數(shù)你如果用new 的方式去使用贰镣,那么他就是一個構(gòu)造函數(shù)。
- 為了區(qū)別膳凝,如果一個函數(shù)想作為構(gòu)造函數(shù)碑隆,作為國際慣例,最好把這個構(gòu)造函數(shù)的首字母大寫蹬音。