ES6關(guān)于構(gòu)造函數(shù)和繼承有了新的語法疼进,但先分析之前的寫法,有助于理解原理
1.原型鏈
javascript沒有像Java秧廉、C#類的概念伞广,要實現(xiàn)繼承只能是通過“原型(prototype)”這條鏈子將要繼承的對象鏈接起來
//對象
var o={}
o.__proto__===Object.prototype//true
//數(shù)組
var arr=[1,2]
arr.__proto__===Array.prototype //true
Array.prototype.__proto__===Object.prototype //true
Object.prototype.__proto__===null //true
//函數(shù)
function f(){}
f.__proto__===Function.prototype//true
Function.prototype.__proto__===Object.prototype//true
說實話一直困惑__proto__
和prototype
這倆啥關(guān)系拣帽,通過上面的代碼就再清晰不過了__proto__
指向了所繼承的父原型,prototype
指向自己的原型嚼锄,所以通過修改__proto__
可以改變所繼承的原型减拭,但__proto__
并不是語言本身的特性,這是各大廠商具體實現(xiàn)時添加的私有屬性区丑,雖然目前很多現(xiàn)代瀏覽器的 JS 引擎中都提供了這個私有屬性拧粪,但依舊不建議在生產(chǎn)中使用該屬性,避免對環(huán)境產(chǎn)生依賴刊苍。生產(chǎn)環(huán)境中既们,我們可以使用 Object.getPrototypeOf
方法來獲取實例對象的原型,然后再來為原型添加方法/屬性正什。
2.構(gòu)造函數(shù)
function Person(name,age){
this.name=name
this.age=age
this.sayName=function(){
alert(this.name)
}
}
Person.prototype.constructor===Person//true
以上是個構(gòu)造函數(shù)啥纸,我一直覺得就是個函數(shù),但其實它返回的是個對象婴氮,用new
關(guān)鍵字構(gòu)造一個對象斯棒。并且在自己的prototype
上將constructor
屬性指向了自己
var person1=new Person('張三',27)
var person2=new Person('李四',18)
person1.constructor===Person//true
person1.__proto__===Person.prototype//true
person2.__proto__===Person.prototype//true
person1.sayName===person2.sayName//false 這不是我們想要的
每個實例的屬性需要不一樣,因為每個人的名字不一樣主经,但是sayName
需要共用荣暮,可以把需要共用的屬性或方法放在prototype
上,
Person.prototype.sayName2=function(){
alert(this.name)
}
person1.sayName2===person2.sayName2//true
現(xiàn)在Person
上其實有2個屬性罩驻,一個是默認生成的constructor
穗酥,另一個是sayName2
,所以又可以用以下的寫法解釋
Person.prototype={
constructor:Person,
sayName2:function(){
alert(this.name)
}
}
接下來對比以下ES6的新語法class
class Person{
constructor(name,age){//放在構(gòu)造器里面的是 不公用的
this.name=name
this.age=age
this.sayName=function(){
alert(this.name)
}
}
//放在構(gòu)造器外面的是公用的
sayName2(){
alert(this.name)
}
}
咋樣惠遏,就感覺是把之前的寫法合并起來了
3.繼承
//父構(gòu)造函數(shù)
function SuperType(name){
this.name=name
}
SuperType.prototype.sayName=function(){
alert(this.name)
}
//子構(gòu)造函數(shù)
function SubType(name,age){
SuperType.call(this,name)
this.age=age
}
var sub=new SubType('張三',12)
sub.name//"張三"
sub.sayName//undefined
sub.__proto__===SubType.prototype//true
SubType.prototype.__proto__===SuperType.prototype//false
先用call
方法繼承構(gòu)造器里的屬性砾跃,此時的關(guān)系為,sub——>SubType——>Object
节吮,要實現(xiàn)繼承要把SubType.prototype.__proto__===SuperType.prototype
你可能想這么寫 SubType.prototype = SuperType.prototype
抽高,但這樣是不行的,他們倆公用一個原型的話透绩,SubType
就沒有存在的意義了翘骂,我們可以用下面的代碼
function F(){}//先創(chuàng)建了一個臨時性的構(gòu)造函數(shù)
F.prototype = SuperType.prototype;//將父原型作為臨時構(gòu)造函數(shù)的原型
var prototype=new F()//這邊也可以直接用new SuperType(),但這樣加上上面的call方法會導(dǎo)致2次調(diào)用SuperType
prototype.contructor=SubType
SubType.prototype=prototype//然后將臨時構(gòu)造函數(shù)的實例作為子構(gòu)造函數(shù)的原型
SubType.prototype.__proto__===SuperType.prototype//true
下面來看看ES6繼承的寫法
class SuperType{
constructor(name){
this.name=name
}
sayName(){
alert(this.name)
}
}
class SubType extends SuperType{
constructor(name,age){
super(name)//必須調(diào)用下父類
this.age=age
}
sayAge(){
alert(this.age)
}
}
var sub=new SubType('張三',12)
sub.__proto__===SubType.prototype//true
SubType.prototype.__proto__===SuperType.prototype//true
注意這邊會多一個聯(lián)系帚豪,這個是ES6特有的
SubType.__proto__===SuperType//true
這樣的結(jié)果是因為碳竟,類的繼承是按照下面的模式實現(xiàn)的。
class SuperType {
}
class SubType {
}
// B 的實例繼承 A 的實例 B.prototype.__proto__=A.prototype
Object.setPrototypeOf(SubType.prototype, SuperType.prototype);
// B 繼承 A 的靜態(tài)屬性 B.__proto__=A
Object.setPrototypeOf(SubType , SuperType );
這兩條繼承鏈狸臣,可以這樣理解:作為一個對象莹桅,子類(SubType
)的原型(__proto__
屬性)是父類(SuperType
);作為一個構(gòu)造函數(shù)固棚,子類(SubType
)的原型對象(prototype
屬性)是父類的原型對象(prototype
屬性)的實例统翩。