1. 程式人生 > >JavaScript——內嵌函式定義類內方法的弊端以及解決辦法(prototype)

JavaScript——內嵌函式定義類內方法的弊端以及解決辦法(prototype)

由於JavaScript是一門弱語言,函式是它的“一等公民”,定義函式的同時也定義了一個類,這一點和其他強語言不同,定義類內方法也和別的不一樣。下面看一段程式碼。

<script type="text/javascript">
	// 建立Person函式
	function Person(name , age)
	{
		this.name = name;
		this.age = age;
		// 為Person物件指定info方法
		this.info = function()
		{
			// 輸出Person例項的name和age屬性
			document.writeln("姓名:" + this.name);
			document.writeln("年齡:" + this.age);
		}
	}
	// 建立Person例項p1
	var p1 = new Person('yeeku' , 29);
	// 執行p1的info方法
	p1.info();
	document.writeln("<hr />");
	// 建立Person例項p2
	var p2 = new Person('wawa' , 20);
	// 執行p2的info方法
	p2.info();
</script>

上面的程式碼在定義Person函式的同時,也定義了一個Person類,而且該函式就是該類的構造器。此外,該構造器不僅為Person例項完成了屬性的初始化,還為Person例項定義了一個info方法。

這個手法看著和java很像,它們在定義類的時候也會定義類內方法,JavaScript是不是也和Java一樣,可以直接這樣定義呢?

答案是可以的。上面的程式碼可以執行,也能夠得到正確的結果。但是,雖然它們可以成功執行,由於JavaScript中函式的特殊性,這種方法有很大的弊端。主要有以下兩個原因:

效能低下:因為每次建立Person例項的時候,程式依次向下執行,每次執行程式都將建立一個新的info函式。當建立多個Person物件的時候,系統會有很多個info函式,這會導致系統記憶體洩漏,從而引起效能下降

。實際上,info函式只要一個就夠了。

使得info函式中的區域性變數產生閉包:閉包會擴大區域性變數的作用域,使得區域性變數一直存活到函式之外的地方。

至於什麼是閉包,下面舉一個例子:

<script type="text/javascript">
	// 建立Person函式
	function Person()
	{
		// locVal是個區域性變數,原本應該該函式結束後立即失效
		var locVal = '瘋狂Java聯盟';
		this.info = function()
		{
			// 此處會形成閉包
			document.writeln("locVal的值為:" + locVal);
			return locVal;
		}
	}
	var p = new Person();
	// 呼叫p物件的info()方法
	var val = p.info();
	// 輸出val返回值,該返回值就是區域性變數locVal。
	alert(val);
</script>

從上面程式碼中可以看出,由於info函式裡訪問了區域性變數locVal,所以行程餓閉包,從而導致locVal變數的作用域被擴大,因此即使離開了info函式,程式依然可以訪問到區域性變數的值。

既然直接用內嵌函式定義類內方法有這麼大的弊端,那麼該如何定義類內方法呢?

這時候就需要用到prototype屬性了。

JavaScript的所有類(也就是函式)都有一個prototype屬性,如果為JavaScript類的prototype屬性增加屬性、方法,則可以視為對原有類的擴充套件。

對於上面這句話,我們可以理解為:增加了prototype屬性的類繼承了原有的類——這就是JavaScript中的一種偽繼承機制。

我們看下面這段程式碼:

<script type="text/javascript">
//定義一個Person函式,同時也定義Person類。
function Person(name,age)
{
	//將區域性變數name,age,賦值給市裡屬性name,age;
	this.name=name;
	this.age=age;
	//使用內嵌函式定義了Person類的方法
	this.info = function()
		{
			// 輸出Person例項的name和age屬性
			document.writeln("姓名:" + this.name);
			document.writeln("年齡:" + this.age);
		}
}
//建立Person的例項p1;
var p1=new Person('Seachin',17);
//執行Person的info方法,
p1.info();
//將walk方法增加到Person的prototype屬性上
Person.prototype.walk=function()
{
	document.writeln(this.name+'正溜達呢......<br/>')
}
document.writeln('<hr/>');
var p2=new Person('SearchinHAHA',18);
//執行p2的info方法;
p2.info();
//執行p2的walk方法;
p2.walk();
//此時p1也具有了walk方法——JavaScript允許為類動態增加方法和屬性
//執行p1的walk方法
p1.walk();
</script>

上面程式碼為Person類的prototype屬性增加了walk函式,即可認為程式為Person類動態增加了walk例項方法。

實際上,JavaScript是一門動態語言,它不經可以為物件動態增加屬性和方法,也可以動態為類增加屬性和方法。

上面程式中採用prototype為Person類增加了一個walk方法,這樣會讓所有的Person例項共享一個walk方法,而且該walk方法不在Person函式之內,因此不會產生閉包。

注意:與Java等真正面向物件的繼承不同,JavaScript並沒有提供真正的繼承。雖然使用prototype屬性可以為一個類動態增加屬性和方法,這可被當成一種“偽繼承”;但這種“偽繼承”的實質是對原有類的修改,並不是產生了一個新的子類,因此原有的那個沒有walk方法的Person類將不再存在。

好啦,以上就是使用內嵌函式定義類內方法的弊端以及解決辦法,如果有什麼疑問或者好的見解,歡迎留言評論,我們一起學習呀。