繼承
繼承是 OOP 語言中的一個(gè)最為人津津樂道的概念。許多OOP 語言都支持兩種繼承方式:接口繼承和實(shí)現(xiàn)繼承。接口繼承只繼承方法簽名减牺,而實(shí)現(xiàn)繼承則繼承實(shí)際的方法。如前所述存谎,由于函數(shù)沒有簽名拔疚,在ECMAScript 中無法實(shí)現(xiàn)接口繼承。ECMAScript 只支持實(shí)現(xiàn)繼承既荚,而且其實(shí)現(xiàn)繼承主要是依靠原型鏈來實(shí)現(xiàn)的稚失。
原型鏈
ECMAScript 中描述了原型鏈的概念,并將原型鏈作為實(shí)現(xiàn)繼承的主要方法恰聘。其基本思想是利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法句各。
構(gòu)造函數(shù)、原型和實(shí)例的關(guān)系:每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象晴叨,原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)的指針凿宾,而實(shí)例都包含一個(gè)指向原型對(duì)象的內(nèi)部指針。那么篙螟,假如我們讓原型對(duì)象等于另一個(gè)類型的實(shí)例菌湃,結(jié)果會(huì)怎么樣呢?顯然遍略,此時(shí)的原型對(duì)象將包含一個(gè)指向另一個(gè)原型的指針惧所,相應(yīng)地,另一個(gè)原型中也包含著一個(gè)指向另一個(gè)構(gòu)造函數(shù)的指針绪杏。假如另一個(gè)原型又是另一個(gè)類型的實(shí)例下愈,那么上述關(guān)系依然成立,如此層層遞進(jìn)蕾久,就構(gòu)成了實(shí)例與原型的鏈條势似。這就是所謂原型鏈的基本概念。
說明
- 定義Son構(gòu)造函數(shù)后僧著,我們沒有再使用Son的默認(rèn)原型履因,而是把他的默認(rèn)原型更換成了Father類型對(duì)象。
- 這時(shí)盹愚,如果這樣訪問 son1.name, 則先在son1中查找name屬性栅迄,沒有然后去他的原型( Father對(duì)象)中找到了,所以是"馬云"皆怕。
- 如果這樣訪問 son1.giveMoney(), 先在son1中找這個(gè)方法毅舆,找不到去他的原型中找西篓,仍然找不到,則再去這個(gè)原型的原型中去找憋活,然后在 Father的原型對(duì)象中 找到了岂津。
- 從圖中可以看出來,在訪問屬性和方法的時(shí)候悦即,查找的順序是這樣的:對(duì)象->原型->原型的原型->...->原型鏈的頂端吮成。 就像一個(gè)鏈條一樣,這樣 由原型連成的"鏈條"盐欺,就是我們經(jīng)常所說的原型鏈赁豆。
- 從上面的分析可以看出仅醇,通過原型鏈的形式就完成了JavaScript的繼承
默認(rèn)頂端原型
在 JavaScript 中所有的類型如果沒有指明繼承某個(gè)類型冗美,則默認(rèn)是繼承的 Object 類型。這種 默認(rèn)繼承也是通過原型鏈的方式完成的析二。
下面的圖就是一個(gè)完整的原型鏈:
說明:
- 原型鏈的頂端一定是Object這個(gè)構(gòu)造函數(shù)的原型對(duì)象粉洼。這也是為什么我們隨意創(chuàng)建一個(gè)對(duì)象,就有很多方法可以調(diào)用叶摄,其實(shí)這些方法都是來自O(shè)bject的原型對(duì)象属韧。
- 通過對(duì)象訪問屬性、方法的時(shí)候蛤吓,一定是會(huì)通過原型鏈來查找的宵喂,直到原型鏈的頂端农猬。
- 一旦有了繼承毫深,就會(huì)出現(xiàn)多態(tài)的情況羞迷。假設(shè)需要一個(gè)Father類型的數(shù)據(jù)玫氢,那么你給一個(gè)Father對(duì)象芹血,或Son對(duì)象都是沒有任何問題的屯碴。而在實(shí)際執(zhí)行的過程中蚁滋,一個(gè)方法的具體執(zhí)行結(jié)果憔购,就看在原型鏈中的查找過程了泼疑。給一個(gè)實(shí)際的Father對(duì)象則從Fahter的原型鏈中查找德绿,給一個(gè)實(shí)際的Son則從Son的原型鏈中查找。
- 因?yàn)槔^承的存在退渗,Son的對(duì)象移稳,也可以看出Father類型的對(duì)象和Object類型的對(duì)象。 子類型對(duì)象可以看出一個(gè)特殊的父類型對(duì)象会油。
幾種測(cè)試數(shù)據(jù)的類型
1.typeof:一般用來測(cè)試簡(jiǎn)單數(shù)據(jù)類型和函數(shù)的類型个粱。
2.instanceof: 用來測(cè)試一個(gè)對(duì)象是不是屬于某個(gè)類型。結(jié)果為boolean值钞啸。
3.isPrototypeOf( 對(duì)象 ) : 這是個(gè) 原型對(duì)象 的方法几蜻,參數(shù)傳入一個(gè)對(duì)象喇潘,判斷參數(shù)對(duì)象是不是由這個(gè)原型派生出來的。 也就是判斷這個(gè)原型是不是參數(shù)對(duì)象原型鏈中的一環(huán)梭稚。
4.函數(shù)借調(diào) Object.prototype.toString.call()
<script>
function Father () {
}
function Son () {
}
Son.prototype = new Father();
var son = new Son();
console.log(son instanceof Son); // true
// Son通過原型繼承了Father
console.log(son instanceof Father); // true
//Father又默認(rèn)繼承了Objcet
console.log(son instanceof Object); // true
console.log(Son.prototype.isPrototypeOf(son))//true
console.log(typeof new Son())//object
console.log(Object.prototype.toString.call(/a/gi))//借調(diào)可以檢測(cè)出它的類型 RegExp
console.log(Object.prototype.toString.call([]))//返回Array
console.log(Object.prototype.toString.call(function () {
}))//返回function
var msg=Object.prototype.toString.call([])
console.log(/[ ](\w+)]/gi.exec(msg))//用正則表達(dá)式輸出類型
</script>
借用構(gòu)造函數(shù)調(diào)用"繼承"
借用構(gòu)造函數(shù)調(diào)用 繼承颖低,又叫偽裝調(diào)用繼承或冒充調(diào)用繼承。雖然有了繼承兩個(gè)字弧烤,但是這種方法從本質(zhì)上并沒實(shí)現(xiàn)繼承忱屑,只是完成了構(gòu)造方法的調(diào)用而已。
使用 call 或 apply 這兩個(gè)方法完成函數(shù)借調(diào)暇昂。這兩個(gè)方法的功能是一樣的莺戒,只有少許的區(qū)別(暫且不管)。功能都是更改一個(gè)構(gòu)造方法內(nèi)部的 this 指向到指定的對(duì)象上急波。
<script>
function foo(name,age) {
this.name=name;
this.age=age;
}
var obj={};
//函數(shù)借調(diào) 借用call
foo.call(obj,"張三","20")//借調(diào)了foo的this,此處的this就指向obj,所以obj就多了
//foo的屬性
console.log(obj)
</script>
組合繼承
組合函數(shù)利用了原型繼承和構(gòu)造函數(shù)借調(diào)繼承的優(yōu)點(diǎn)从铲,組合在一起。成為了使用最廣泛的一種繼承方式澄暮。
<script>
function Animal(name,color) {
this.name=name;
this.color=color;
}
Animal.prototype.eat=function () {
console.log("動(dòng)物在吃")
}
function Dog(name,color,weight) {
Animal.call(this,name,color);//借調(diào)animal中的this屬性
//this是Animal的this 只是指向改變 指向了dog
this.weight=weight;
}
Dog.prototype=new Animal();
Dog.prototype.constructor=Dog;
var dog=new Dog("黑子","黑色",20)
console.log(dog)
dog.eat()
console.log(dog instanceof Animal)
</script>
說明
- 組合繼承是我們實(shí)際使用中最常用的一種繼承方式名段。
- 在子類型的構(gòu)造函數(shù)中借調(diào)了父類型的構(gòu)造函數(shù),也就是說泣懊,子類型的原型(也就是Father的對(duì)象)中有的屬性伸辟,都會(huì)被子類對(duì)象中的屬性給覆蓋掉。