標籤:ons strong 構造器 使用 技術 也有 log object 構造
原型對象
每個javascript對象都有一個原型對象,這個對象在不同的解譯器下的實現不同。比如在firefox下,
每個對象都有一個隱藏的__proto__屬性,這個屬性就是“原型對象”的引用。
原型鏈
由於原型對象本身也是對象,根據上邊的定義,它也有自己的原型,而它自己的原型對象又可以有自
己的原型,這樣就組成了一條鏈,這個就是原型鏈,JavaScritp引擎在訪問對象的屬性時,如果在對象本
身中沒有找到,則會去原型鏈中尋找,如果找到,直接傳回值,如果整個鏈都遍曆且沒有找到屬性,則返
回undefined.原型鏈一般實現為一個鏈表,這樣就可以按照一定的順序來尋找。
我們先用一個構造器來實現一個建構函式:
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屬性,而執行個體化出來的對象會擁有__proto__,而不會有prototype。
像畫的那樣,兩個執行個體化的對象都通過__proto__屬性指向了A.prototype(即建構函式的原型對象)
而原型對象的__proto__指向Object對象,就像a.toString()的toString方法就是存在於Object原型對象(Object.prototype)中。
so:當使用對象的方法或屬性時,對象會在一步一步通過__proto__向上尋找,找到最近的則是最終的擷取到的方法或屬性。
————這就是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.prototypevar 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; //truearr.constructor === Array; //truefunction TEMP(){}var temp = new TEMP();temp instanceof TEMP; //truetemp.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()
Js 原型對象與原型鏈