javascript在創(chuàng)建一個(gè)對(duì)象的時(shí)候余爆,默認(rèn)會(huì)為該對(duì)象創(chuàng)建一個(gè)prototype的屬性,該屬性的值是一個(gè)對(duì)象夸盟,即創(chuàng)建對(duì)象的原型蛾方。
下面我們通過(guò)一個(gè)例子了解原型的特性:
function Person(){
}
Person.prototype.name="tom"
var person1 = new Person()
console.log(person1.name) //tom
var person2 = new Person()
console.log(person2.name) //tom
上面的例子中,構(gòu)造函數(shù)Person的原型對(duì)象有一個(gè)自定義屬性name
所有的實(shí)例共享同一個(gè)原型對(duì)象,所以打印person1.name和person2.name都是tom
構(gòu)造函數(shù)上陕、原型和實(shí)例的關(guān)系:
每一個(gè)構(gòu)造函數(shù)(函數(shù)對(duì)象)都有一個(gè)prototype屬性桩砰,指向函數(shù)的原型對(duì)象;每一個(gè)原型對(duì)象都有一個(gè)constructor屬性释簿,指向構(gòu)造函數(shù)五芝;每一個(gè)實(shí)例都有一個(gè)__proto__屬性,指向構(gòu)造函數(shù)的原型對(duì)象
那么原型對(duì)象辕万、prototype對(duì)象、實(shí)例對(duì)象之間的關(guān)系圖下圖所示
上圖中prototype對(duì)象也是一個(gè)實(shí)例對(duì)象沉删,他也有自己的原型對(duì)象渐尿,默認(rèn)原型的構(gòu)造函數(shù)是Object,加上prototype對(duì)象關(guān)系圖如下:
接下來(lái)我們來(lái)說(shuō)一下繼承
1.原型鏈繼承
function?Animal()?{
??this.sleep?=?function()?{
????console.log('睡覺(jué)')
??}
}
function?Person(name)?{
??this.name?=?name
}
Person.prototype?=?new?Animal()
var?liming?=?new?Person('liming')
console.log(liming.name)
liming.sleep()
打印結(jié)果
2
睡覺(jué)
上述例子中矾瑰,改變了Person構(gòu)造函數(shù)的原型指向砖茸,從而實(shí)現(xiàn)了繼承,讓Person的實(shí)例liming擁有了sleep的方法殴穴。上述例子的圖解如下:
缺點(diǎn):如果Animal實(shí)例中有引用類(lèi)型的變量凉夯,這個(gè)引用變量被所有實(shí)例共享,一個(gè)實(shí)例更改之后會(huì)影響其他實(shí)例
如果我們將代碼改成
function?Animal()?{
??this.todoList?=?['坐車(chē)',?'開(kāi)會(huì)',?'吃飯',?'回家']
??this.sleep?=?function()?{
????console.log('睡覺(jué)')
??}
}
function?Person(name)?{
??this.name?=?name
}
Person.prototype?=?new?Animal()
var?liming?=?new?Person('liming')
console.log(liming.name)
liming.todoList.splice(1,?1)
var?tom?=?new?Person('tom')
console.log(tom.todoList)
可以看到liming的代辦去掉了開(kāi)會(huì)采幌,tom的代辦的開(kāi)會(huì)也沒(méi)有了劲够。而且沒(méi)辦法給Animal傳遞實(shí)例參數(shù)。所以有了構(gòu)造函數(shù)繼承
二休傍、構(gòu)造函數(shù)繼承
構(gòu)造函數(shù)繼承就是在子類(lèi)的構(gòu)造函數(shù)中執(zhí)行父類(lèi)的構(gòu)造函數(shù)征绎,并使用call改變父類(lèi)的this指向子類(lèi),還可以使用call傳遞參數(shù)磨取,實(shí)現(xiàn)實(shí)例屬性人柿,具體代碼如下
function?Animal(sort)?{
??this.sort?=?sort
??this.todoList?=?['坐車(chē)',?'開(kāi)會(huì)',?'吃飯',?'回家']
??this.sleep?=?function()?{
????console.log('睡覺(jué)')
??}
}
function?Person(name,?sort)?{
??Animal.call(this,?sort)
??this.name?=?name
}
var?liming?=?new?Person('liming',?'people')
liming.todoList.splice(1,?1)
console.log('liming的todolist',?liming.todoList)
var?tom?=?new?Person('tom',?'people')
console.log(tom.sort)
console.log('tom的todolist',?tom.todoList)
? ? 結(jié)果展示:
上述例子在Person的構(gòu)造函數(shù)中執(zhí)行了Animal.call(this,sort)? 執(zhí)行力Animal構(gòu)造函數(shù)的代碼,并把this忙厌,指向當(dāng)前person實(shí)例凫岖,所以liming去掉了開(kāi)會(huì)日程,但是并不影響tom的代辦列表逢净。
原型關(guān)系圖解:(這個(gè)例子Person的原型指向并沒(méi)有改變哥放,只是把Person的代碼拿來(lái)執(zhí)行了一遍歼指。)
缺點(diǎn):
這種方法的缺點(diǎn)顯而易見(jiàn),沒(méi)有實(shí)現(xiàn)繼承婶芭。
三东臀、原型構(gòu)造函數(shù)組合繼承
function?Animal(sort)?{
??this.sort?=?sort
??this.todoList?=?['坐車(chē)',?'開(kāi)會(huì)',?'吃飯',?'回家']
??this.sleep?=?function()?{
????console.log('睡覺(jué)')
??}
}
Animal.prototype.saySort?=?function()?{
??console.log(this.sort)
}
function?Person(name,?sort)?{
??Animal.call(this,?sort)
??this.name?=?name
}
Person.prototype?=?new?Animal()
var?liming?=?new?Person('liming',?'people')
liming.todoList.splice(1,?1)
console.log('liming的todolist',?liming.todoList)
var?tom?=?new?Person('tom',?'people')
tom.saySort()
顧名思義是前兩種方式的結(jié)合,在構(gòu)造函數(shù)的基礎(chǔ)上使用Person.prototype?=?new?Animal() 改變Person的prototype的指向犀农。從而是可以訪問(wèn)Animal原型上的方法saySort()
關(guān)系圖解:
可以清晰的看到 liming和tom都有自己的todolist 所以不會(huì)去原型上查找todolist屬性惰赋。
這種方法的缺點(diǎn):
每個(gè)實(shí)例都有一份自己的屬性,原型上也有一份屬性呵哨,雖然實(shí)例上有就不會(huì)去原型上查找赁濒,但是這種方法原型上的屬性和方法就沒(méi)有用,有些多余孟害。
4.原型式繼承
創(chuàng)建一個(gè)名為F的構(gòu)造函數(shù)拒炎,把 F的prototype設(shè)置為傳進(jìn)來(lái)的對(duì)象
var?person?=?{
??name:?'tom',
??sayName:?function()?{
????console.log(this.name)
??}
}
function?object(obj)?{
??function?F()?{}
??F.prototype?=?obj
??return?new?F()
}
var?person1?=?object(person)
person1.name?=?'jerry'
person1.sayName()
var?person2?=?object(person)
person2.name?=?'jack'
person1.sayName()
打印結(jié)果:
結(jié)果很明顯所有實(shí)例共享原型屬性
關(guān)系圖解:
缺點(diǎn):所有實(shí)例共享原型,而且沒(méi)有自己的實(shí)例屬性挨务。
5.寄生式繼承
在原型式繼承的基礎(chǔ)上击你,封裝原型式繼承
function?create(obj,?suject)?{
??var?example?=?Object(obj)
??example.suject?=?suject
??return?example
}
var?person?=?{
??name:?'tom',
??sayName:?function()?{
????console.log(this.name)
??}
}
var?person1?=?create(person,?'游泳')
console.log(person1.suject)
person1.name?=?'jerry'
var?person2?=?create(person,?'拳擊')
console.log(person2.suject)
person2.sayName()
打印結(jié)果如下:
顯而易見(jiàn),person1和person2擁有了自己的實(shí)例屬性谎柄,
Object(person) 的作用等同于原型式繼承 把person作為構(gòu)造函數(shù)的原型丁侄,所有的實(shí)例共享person的屬性和方法
6.寄生組合式繼承
就是把寄生式繼承和組合式繼承結(jié)合起來(lái)
function?inherite(parent,?child)?{
??var?pro?=?Object.create(parent.prototype)
??pro.construct?=?child
??child.prototype?=?pro
}
function?Animal(sort)?{
??this.sort?=?sort
??this.todoList?=?['坐車(chē)',?'開(kāi)會(huì)',?'吃飯',?'回家']
??this.sleep?=?function()?{
????console.log('睡覺(jué)')
??}
}
Animal.prototype.saySort?=?function()?{
??console.log(this.sort)
}
function?Person(name,?sort)?{
??Animal.call(this,?sort)
??this.name?=?name
}
inherite(Animal,?Person)
var?liming?=?new?Person('liming',?'people')
liming.todoList.splice(1,?1)
console.log('liming的todolist',?liming.todoList)
var?tom?=?new?Person('tom',?'people')
tom.saySort()
console.log('tom的todolist',?tom.todoList)
打印結(jié)果
關(guān)系圖解:
這種方式就避免了再次創(chuàng)建Animal實(shí)例,sort todolist? sleep等屬性和方法也只存在在實(shí)例上朝巫,原型上已經(jīng)沒(méi)有了鸿摇。
大多數(shù)的繼承都采用這種方式。