部落格搬家:由于各種原因,我現在的部落格将首發于blog.mojijs.com, 可以百度搜尋 “姜哥的墨迹技術部落格” , 或者 點選這裡 本文位址 http://blog.mojijs.com/post/41.html
本文是JavaScript交流貼的回帖(csdn部落格blog . csdn . net/tt361),因内容較多是以單獨寫成一篇文章,僅為抛磚,希望大家不要珍惜玉。
約定:
中文“原型”指代對象的__proto__屬性指向的對象,原型鍊為__proto__形成的鍊條,prototype為函數的prototype屬性。
關于原型和原型鍊
js中的所有對象都有一個原型,而原型又是一個對象,是以原型也有原型,這就形成了原型鍊。
這句話并不完全正确,原型鍊不是無限長的,必須有一個盡頭。原型鍊的盡頭是Object.prototype所指向的對象,所有對象(除了Object.prototype,Object.prototype沒有原型)原型鍊的盡頭都是Object.prototype,也就是說Object.prototype是除Object.prototype之外所有對象共享的,那麼在Object.prototype中寫入一個屬性在任何對象中都是可通路的,但是這樣做非常危險,慎用。
關于上述闡述可用下面代碼簡單測試。
console.log(Object.prototype == new Object().__proto__); // true
console.log(Object.prototype == new Date().__proto__.__proto__); // true
console.log(Object.prototype == Object.__proto__.__proto__); // true
function C(){ }
var c = new C();
console.log(Object.prototype == C.__proto__.__proto__); // true
console.log(Object.prototype == c.__proto__.__proto__); // true
var o = {};
console.log(Object.prototype == o.__proto__); // true
Object.prototype.tt361 = "my CSDN web log";
console.log(Object.tt361); // my CSDN web log
console.log(new Date().tt361); //my CSDN web log
說明:
對于chrome,對象的__proto__屬性指向該對象的原型,該屬性并不建議(不應該)在實際程式設計中使用。
原型鍊描繪了屬性查找路徑,js對象的屬性查找規則如下:
1、在對象本身查找是否存在某屬性,如果存在則停止查找并傳回。
2、如果沒找到則在原型中查找,如果存在則停止查找并傳回。
3、如果仍沒找到則在原型的原型中查找,如果存在則停止查找并傳回。
4、重複步驟3直到找到該屬性或周遊到Object.prototype,如果周遊到原型鍊盡頭(Object.prototype)依然無法找到則傳回undefined
js構造函數與原型繼承
js函數本身就是對象(Object是一個函數),那麼就是說js函數具有對象的所有特性。将函數作為構造函數使用(new操作符加函數,如new Object())可以生成新對象,新對象的原型(新對象的__proto__屬性)是構造函數的prototype屬性(并不是構造函數的__proto__)。如果使用同一構造函數建立多個對象,一般來說所有對象共享同一個原型(不共享的情況點選此連結 修改構造函數原型——JavaScript中的對象(二))。
js構造函數就是js的普通函數,隻不過一般來說将首字母大寫以示差別。js的繼承基本靠原型(還是有一些其他方法的,本文僅寫原型),根據上文的描述,使用構造函數建立的對象的原型是構造函數的prototype屬性所代表的對象,是以繼承需要在prototype上下功夫。
舉例如下:
function S(name){
this.name = name; // public變量
var mobilephone; // private變量
this.getMobilephone = function(){ // 此類方法會被建立多次,建立次數與對象數目相同
return mobilephone;
}
this.setMobilephone = function(arg){
mobilephone = arg;
}
this.total++;
}
// 父類構造函數prototype
S.prototype.total = 0 ; // 各對象共享的變量
S.prototype.getTotal = function(){ // 各對象共享的方法,該類方法僅建立一次
return this.total;
}
// 子類構造函數聲明
function C(name){
S.call(this, name); // 構造函數和普通函數無異,借用父類構造函數做初始化。
var age;
this.getAge = function(){
return age;
}
this.setAge = function(arg){
age = arg;
}
}
// 子類構造函數prototype,子類prototype指向父類構造函數建立的對象主要是為了獲得父類的prototype中的屬性和方法。
C.prototype = new S();
C.prototype.constructor = C;
// C. 類變量
C.blog = "tt361";
var c = new C("name is tt361");
var c2 = new C("name is tt362");
c.setMobilephone("13JQQQQKKAA");
console.log(c.getMobilephone()); // 13JQQQQKKAA
console.log(c.getTotal()); // 2
console.log(c.name); // name is tt361
console.log(c instanceof C); // true
console.log(c instanceof S); // true
對于函數如果能寫到原型中盡量寫到原型中,寫到原型中的函數僅建立一次,寫到構造函數函數體中的函數會被建立多次。
js面向對象涉及到哪些變量
1.私有屬性。上例構造函數S的mobilephone屬性為私有屬性,實作原理是閉包。
2.公有屬性。在構造函數體内通過this設定的屬性,因為構造函數中的this是指向正在建立的對象。
3.類變量。因為構造函數本身就是對象,給構造函數設定屬性就是類屬性。
4.所有對象共享但類不可通路的屬性。一般情況下同一構造函數建立的多個對象共享同一執行個體,是以寫在prototype中的屬性所有對象共享。正如之前描述的“對象的原型指向是構造函數的prototype”,是以這種類型的屬性并不是構造函數的屬性。
js的多繼承一般靠mixin實作,即屬性複制,這就意味着正真的父類隻有一個。
dojo的繼承實作了C3算法,多繼承時僅排序第一位的是真正的父類,其餘的均mixin。
其他文章連結
你自認為了解了JavaScript?
JavaScript交流貼
js實作面向切面的程式設計(AOP)
JavaScript中的對象(一)
消除JavaScript中的if