用JavaScript寫類別的淺薄心得 - Class篇

用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的心得分享單純這次的總結吧

 

--

本文可能有理解錯誤  或不盡不實的地方

請路過的前輩不要客氣  用力打醒

這會是我們成長的主要養分