原型(prototype)在js中但是擔當着舉足輕重的做用,原型的實現則是在原型鏈的基礎上,理解原型鏈的原理後,對原型的使用會更加自如,也能體會到js語言的魅力。函數
本文章會涉及的內容this
咱們先用一個構造器來實現一個構造函數:spa
function A(){ this.mark = "A"; this.changeMark = function(){ this.mark += "_changed"; } } A.prototype.mark2 = "A2"; A.prototype.changeMark2 = function(){ this.mark2 += "_changed"; } var a = new A(); var a2 = new A(); //下面則說明構造函數實例化後,分配着不一樣的實例對象,互不相關 console.log(a.mark); //"A" console.log(a2.mark); //"A" a.changeMark(); //使用實例對象中的方法 console.log(a.mark); //"A_changed" console.log(a2.mark); //"A" //下面則說明了new操做符的一項做用,即將原型中的this指向當前對象, //在a.changeMark2執行時,changMark2中的方法先找 this.mark2 的值, //可是實例對象this中沒有mark2值,則在原型鏈(後面會介紹)向上尋找,獲得A原型對象中的mark2值, //在賦值時,將修改後的值添加在了a實例中。 //總:雖然調用的是prototype方法,可是不會對prototype屬性作修改,只會說是在實例中新增屬性,可是在使用時,會最使用最近獲得的屬性(在後面原型鏈中能夠加以理解) console.log(a.mark2); //"A2" console.log(a2.mark2); //"A2" a.changeMark2(); //使用原型鏈中的方法 console.log(a.mark2); //"A2_changed" console.log(a2.mark2); //"A2"
爲何a可使原型中的changeMark2方法?這就和js巧妙的原型鏈相關,在Firefox中咱們能夠打印出對象並可查看到對象下面的__proto__。prototype
咱們把上面的過程用流程圖來表示:code
只有構造函數纔會有prototype屬性,而實例化出來的對象會擁有__proto__,而不會有prototype。對象
就像上圖畫的那樣,兩個實例化的對象都經過__proto__屬性指向了A.prototype(即構造函數的原型對象)blog
而原型對象的__proto__指向Object對象,就像a.toString()的toString方法就是存在於Object原型對象(Object.prototype)中。繼承
so:當使用對象的方法或屬性時,對象會在一步一步經過__proto__向上尋找,找到最近的則是最終的獲取到的方法或屬性。ip
————這就是js中的原型鏈。原型鏈
就像圖上看到的同樣,全部對象的原型鏈最終都指向了Object對象,而Object的原型對象(Object.prototype)是爲數很少的不繼承自任何屬性的對象,即Object.prototype沒有__proto__,是原型鏈的頂峯。
經過上面咱們能夠了解到,當咱們對A.prototype或Object.prototype添加屬性或方法時,在a和a2實例中都會查看到該屬性或方法,由於這兩個實例都經過原型鏈與A和Object的原型對象相連。
再來看看原型對象和原型鏈在繼承方面的實現:
再構造一個函數A和一個函數B,並讓B繼承A,以下:
function A(mark){ this.mark = mark; } A.prototype.getMark = function(){ return this.mark; } function B(mark){ this.mark = mark } //var temp = new A("A"); //B.prototype = temp; //上面語句和下語句做用相同 B.prototype = new A("A"); //實例化一個A,其賦值於B.prototype
//咱們知道了原型鏈,這樣作就是將B的原型對象與A的原型對象經過原型鏈(__proto__)鏈接起來
//以實現繼承
var b = new B("B"); console.log(b.mark); //B, 結果如上面原型鏈分析的那樣,向上找到最近的屬性,則爲b實例中的mark:"B"
其中的結構示意大概以下圖:
這時咱們能夠看到,在B.prototype中是沒有constructor的,由於B.prototype只是簡單的new A("A")對象賦值的結果。
在js中的constructor有什麼做用呢?如:
var arr = new Array(); arr instanceof Array; //true arr.constructor === Array; //true function TEMP(){ } var temp = new TEMP(); temp instanceof TEMP; //true temp.constructor === TEMP; //true
引用《JavaScript權威指南》中的對於constructor的解釋爲:對象一般繼承的constructor均指代它們的構造函數,而構造函數是類的「公共標識」。即constructor可用來判斷對象所屬的類。
在上面的小例子中,用instanceof也可判斷對象的類,可是有自身的缺陷,instanceof的實現方法爲:
instanceof不會去檢查temp是否是由TEMP()構造函數初始化的,面是判斷temp是否繼承自TEMP.prototype,這樣,範圍就寬了不少。
如在上面的大例中,使用
b instaceof B //true 由於在b的原型鏈中能夠找到B.prototype對象 b instaceof A //true 在b的原型鏈中也能夠找到A.prototype對象
能夠說instanceof是用來檢測繼承關係的。
而當
console.log(b.constructor) //function A() //由於在b的原型鏈中,最近的constructor就是A.prototype中有constructor指向了構造函數A();
但咱們知道的b是屬於B類的,那最後因此要作的就是:
B.prototype.constructor = B; //將constructor指向自身的構造函數 var new_b = new B("B"); console.log(new_b.constructor) //function B()
這樣,一個完整的類繼承才完成了。
最後附上一個完整繼承後的結果圖:
第一次寫長文,若有問題,還請你們指出。
轉載請註明出處,謝謝。
Finish.