javascript在創(chuàng)建一個(gè)對(duì)象的時(shí)候,默認(rèn)會(huì)為該對(duì)象創(chuàng)建一個(gè)prototype的屬性,該屬性的值是一個(gè)對(duì)象,即創(chuàng)建對(duì)象的原型。
下面我們通過一個(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)系圖如下:

接下來我們來說一下繼承
1.原型鏈繼承
function?Animal()?{
??this.sleep?=?function()?{
????console.log('睡覺')
??}
}
function?Person(name)?{
??this.name?=?name
}
Person.prototype?=?new?Animal()
var?liming?=?new?Person('liming')
console.log(liming.name)
liming.sleep()
打印結(jié)果
2
睡覺
上述例子中,改變了Person構(gòu)造函數(shù)的原型指向,從而實(shí)現(xiàn)了繼承,讓Person的實(shí)例liming擁有了sleep的方法。上述例子的圖解如下:

缺點(diǎn):如果Animal實(shí)例中有引用類型的變量,這個(gè)引用變量被所有實(shí)例共享,一個(gè)實(shí)例更改之后會(huì)影響其他實(shí)例
如果我們將代碼改成
function?Animal()?{
??this.todoList?=?['坐車',?'開會(huì)',?'吃飯',?'回家']
??this.sleep?=?function()?{
????console.log('睡覺')
??}
}
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的代辦去掉了開會(huì),tom的代辦的開會(huì)也沒有了。而且沒辦法給Animal傳遞實(shí)例參數(shù)。所以有了構(gòu)造函數(shù)繼承
二、構(gòu)造函數(shù)繼承
構(gòu)造函數(shù)繼承就是在子類的構(gòu)造函數(shù)中執(zhí)行父類的構(gòu)造函數(shù),并使用call改變父類的this指向子類,還可以使用call傳遞參數(shù),實(shí)現(xiàn)實(shí)例屬性,具體代碼如下
function?Animal(sort)?{
??this.sort?=?sort
??this.todoList?=?['坐車',?'開會(huì)',?'吃飯',?'回家']
??this.sleep?=?function()?{
????console.log('睡覺')
??}
}
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去掉了開會(huì)日程,但是并不影響tom的代辦列表。
原型關(guān)系圖解:(這個(gè)例子Person的原型指向并沒有改變,只是把Person的代碼拿來執(zhí)行了一遍。)

缺點(diǎn):
這種方法的缺點(diǎn)顯而易見,沒有實(shí)現(xiàn)繼承。
三、原型構(gòu)造函數(shù)組合繼承
function?Animal(sort)?{
??this.sort?=?sort
??this.todoList?=?['坐車',?'開會(huì)',?'吃飯',?'回家']
??this.sleep?=?function()?{
????console.log('睡覺')
??}
}
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的指向。從而是可以訪問Animal原型上的方法saySort()
關(guān)系圖解:

可以清晰的看到 liming和tom都有自己的todolist 所以不會(huì)去原型上查找todolist屬性。
這種方法的缺點(diǎn):
每個(gè)實(shí)例都有一份自己的屬性,原型上也有一份屬性,雖然實(shí)例上有就不會(huì)去原型上查找,但是這種方法原型上的屬性和方法就沒有用,有些多余。
4.原型式繼承
創(chuàng)建一個(gè)名為F的構(gòu)造函數(shù),把 F的prototype設(shè)置為傳進(jìn)來的對(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í)例共享原型,而且沒有自己的實(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é)果如下:

顯而易見,person1和person2擁有了自己的實(shí)例屬性,
Object(person) 的作用等同于原型式繼承 把person作為構(gòu)造函數(shù)的原型,所有的實(shí)例共享person的屬性和方法
6.寄生組合式繼承
就是把寄生式繼承和組合式繼承結(jié)合起來
function?inherite(parent,?child)?{
??var?pro?=?Object.create(parent.prototype)
??pro.construct?=?child
??child.prototype?=?pro
}
function?Animal(sort)?{
??this.sort?=?sort
??this.todoList?=?['坐車',?'開會(huì)',?'吃飯',?'回家']
??this.sleep?=?function()?{
????console.log('睡覺')
??}
}
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)沒有了。
大多數(shù)的繼承都采用這種方式。