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函式中的區域性變數產生閉包:閉包會擴大區域性變數的作用域,使得區域性變數一直存活到函式之外的地方。
至於什麼是閉包,下面舉一個例子:
<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類將不再存在。
好啦,以上就是使用內嵌函式定義類內方法的弊端以及解決辦法,如果有什麼疑問或者好的見解,歡迎留言評論,我們一起學習呀。