用JavaScript寫類別的淺薄心得–Class篇
上一篇落落長講了一堆大家都知道的廢話, 其實不是要混字數
雖然想說要直接切入主題怎麼用JavaScript寫類別
但是一邊寫一邊整理思緒
覺得有的東西不提一下很難繼續, 所以就陸陸續續寫了一堆好像有點相關又不太相關的東西
其實在講的都是一個JavaScript的物件特色
之後才能切到這邊的真正重點
如何用JavaScript的物件特性去模擬寫出一個類別
直接切到問題的重點, 我們可以先想像一個C#的類別會有什麼東西
可能腦袋就冒出一堆東西, 繼承、介面、私有、公開、屬性、存取子、方法、靜態方法、抽象之類的東西
不過對我來說其實沒這麼複雜,一個類別他有的東西就是兩個
建構子、屬性跟方法
不果他屬性是繼承來的也好, 方法是實作出抽象方法的也好
程式在真正操作的時候
就是我先用建構子new一個物件出來,
接著再操作他的屬性跟使用他的方法
有點這樣的感覺之後我們可以直接切進JavaScript來看看他要怎麼做出一個有方法有屬性的類別
( 故意不提什麼繼承、介面之類的東西,其實是我不會 啦啦啦
不過.NET AJAX Library已經有實作出類似的功能了,有興趣的可以google一下 orz)
===================================================================================
假如說我們現在有一個C#的類別如下
public class Student
{
public Student(string szName, int nAge)
{
Name = szName;
Age = nAge;
}
public string Name { set; get; }
public int Age { set; get; }
public string IntroduceMe()
{
return string.Format("我叫做{0}, 今年{1}歲", this.Name, this.Age);
}
}
可以看到有個略顯低能的類別, 不過主要就定義了一個建構子, 兩個屬性, 一個方法
要是搬到JavaScript可以怎麼做呢
var student = function (szName, nAge) {
this.Name = szName;
this.Age = nAge;
};
student.prototype = {
Name: '',
Age: '',
IntroduceMe: function () {
alert('我叫做' + this.Name + ', 今年' + this.Age + '歲');
}
};
可以看到長得跟C#的類別很像
而使用起來就像是
var stu = new student("Herman", 18);
stu.IntroduceMe();
有興趣的人可以直接用瀏覽器debug跑跑看
會看到先進student這個functoin (建構子), 接著把傳入的參數指給Name跟Age(屬性),
而用IntroduceMe則可以操作那些屬性(方法)
於是完成了一個簡單的類別
而下面再針對一些重點做分享, 再請大家指正
==================================================================================
一、可怕的this
可以看到我在IntroduceMe的時候用this取得了Name跟Age
乍看之下似乎就跟C#一模一樣
但是this是程式最容易出錯然後死的不明不白的地方
JavaScript this的定義跟C#的定義其實不盡相同
要詳細討論this其實可以寫成一本書
簡單來說, this可以說是那個屬性或方法的上一層指標 ( 別鞭我, 我知道不能這樣一言弊之, orz)
所以在IntroduceMe的時候, this代表的是Student這個實體物件, 因此可以直接用this.Name存取Student的Name
但是假如我們多加了一個
IntroduceMe: function () {
alert('我叫做' + this.Name + ', 今年' + this.Age + '歲');
$.each([1, 2, 3], function () {
alert(this);
});
}
會發現明明都在IntroduceMe裡面, 可是為什麼each裡面的this卻變成了1,2,3
因為已經跑進了jQuery的each這個屬性裡面, 而他把[1,2,3]這個陣列當成主體, 用each帶進去跑
所以this已經跟Student沒關係
這邊真的超重要, 很容易死得不明不白
那要是我們希望在each裡面仍夠能存取Student要怎麼做呢?
student.prototype = {
Name: '',
Age: '',
IntroduceMe: function () {
alert('我叫做' + this.Name + ', 今年' + this.Age + '歲');
var _this = this;
$.each([1, 2, 3], function () {
alert('我叫做' + _this.Name + ', 這邊的this是' + this);
});
}
};
先把this這個指標存起來, 在each裡面改成存取_this就好
因此建議最好再類別的一開始就宣告一個_this之類的屬性把this給存起來
然後統一操作_this就好
不過也是看個人習慣啦
對了, 附帶一提
<input type="button" id='btnTest' value="Test" />
$('#btnTest').click(stu.IntroduceMe);
這樣也會出錯喔
$('#btnTest').click(function () { stu.IntroduceMe(); });
可是這樣就不會出錯,
為什麼這邊就先賣一個關子, 建議有興趣的朋友直接設中斷點看呼叫IntroduceMe的this是什麼, 就知道為什麼會出錯了
所以請再使用this的時候真的要千萬小心, 好多血淚的教訓啊..
==================================================================================
最後再次強調, JavaScript本身沒有一個對類別的嚴謹定義
各門各派用js寫類別的方法都大不相同, 不過大致上都是操作物件的屬性來做出一個類似類別的方法
我會這樣把他分成兩塊, function跟prototype
是因為在閱讀上很像我們所熟悉的建構子跟方法
但事實上如果你高興, 你也可以寫成
var student = function (szName, nAge) {
this.Name = szName;
this.Age = nAge;
this.IntroduceMe = function () {
alert('我叫做' + this.Name + ', 今年' + this.Age + '歲');
};
};
這個跑起來跟上面一樣的效果,
也就是說完全可以不用prototype
畢竟prototype本來就不是用在這個地方
反正通通是存取this這個東西
在一開始的function存取或是用prototype設定都可以
那我為什麼要這樣做區分?
因為事實上大部分我們需要寫一個類別的時候
那個類別都不會這麼簡單
裡面可能充滿了複雜的程式邏輯跟一堆屬性
要是把所有的屬性通通都寫在一個function裡面東一個西一個
這樣超難維護起
所以我才會借用prototype的特性把屬性跟方法獨立出來
但是並沒有一個規範說一定要這樣寫
就隨自己的習慣規定就好
這篇並沒有提到private的屬性跟方法要怎麼寫
其實是有方法的, 不過我覺得是不太重要也不是說非常必要
有人有興趣再提出來交流好了
最後就用下一篇jQueryUI的心得分享單純這次的總結吧
--
本文可能有理解錯誤 或不盡不實的地方
請路過的前輩不要客氣 用力打醒
這會是我們成長的主要養分