天天看點

js中的原型和繼承

部落格搬家:由于各種原因,我現在的部落格将首發于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

繼續閱讀