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

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

還記得剛開始上C++,  一值不斷強調的重要觀念就是封裝

一直到後來已經脫離不離物件概念去跑程式

 

不管是把常用的工具寫成一個類別

或著是定義好屬性、方法的一個類別

 

在程式裡面總是充滿了這些東西在互相運作

 

 

而在AJAX上面,  其實能的話我們最好也是用類別、物件的感覺去處理程式的片段

不管在維護上或是閱讀上都會比較清楚

 

 

這是一個很深奧的學問,  我自認沒有資格對這個議題去做什麼分享

充其量就是一些個人在使用上的小心得而已

 

或許有些錯誤的地方,  就請再指正了

 

 

其實說穿了 JavaScript本身沒有提供一個完整的架構去實作類別

於是很多人就利用了JavaScript本身充滿彈性的物件類別

 

去模擬做出一個自己的類別, 

方法千百種, 當然各有利弊

 

怎麼作才是一個完美的作法,  其實我也在找

 

 

對我而言會希望一個架構可以很清楚的定義出來屬性在哪邊找,  方法有哪些

建構子寫在哪邊,  如何區隔出static方法與非static的方法

 

一個清楚的架構規範有助於自己編寫程式,  以及好維護

也不會給之後接手的人帶來很大的困擾

 

以下的程式碼只能說是我的一個習慣

就提供做為參考了

 

=================================================================================================

一、靜態的函式庫

 

有的時候我們會寫一個例如說

 

public static class Utility  {...}   一些個人寫程式的小工具


public class Utility
{
	public static  void JavaAlert(Control my, string szMessage)
	{
		ScriptManager.RegisterClientScriptBlock(my, my.GetType(), 
			string.Format("SendMsg{0}", my.ClientID),
			string.Format("alert('{0}');", szMessage.Replace("'", "\\'")), true);
	}
	//.....
}

 

這種類別裡面大部分都是靜態Method,  不需要new出來就可以直接用 Utility.JavaAlert(this, "test")  直接呼叫的個人函式庫

要是對應到JavaScript該怎麼作呢?

 

 

例如說現在假如我們有一個自己寫的小工具,  可以讀出目前的QuertString


function queryStr() {
        var AllVars = window.location.search.substring(1);
        var Vars = AllVars.split("&");
        for (i = 0; i < Vars.length; i++) {
            var Var = Vars[i].split("=");
            if (Var[0] == name) return Var[1];
        }
        return "";
}

 

然後在不同頁面用到就重複寫一次,

或是就算沒重複,  類似功能的小function也散落在專案各處

 

很難管理跟維護

 

 

要是我們想要跟C#一樣用個utiltiy的類別把他裝起來,

首先我們假設有一個叫做global.js,  這個檔案會在MasterPage匯入,  也就是說每一頁都會讀取到

 

 

然後我們可以在裡面加這段程式


var utility = {                  //共用的AjaxOnError函式
    parseId: function (szInput) { //取得數字,若不是數字就回傳-999
        if (szInput == null || szInput == undefined || !szInput) return -999;
        var int = parseInt(szInput, 10);
        return isNaN(int) ? -999 : int;
    },
    alertArgs: function () {      //測試用的函數,把傳入的值用逗號分割後alert
        var msg = '';
        for (var i = 0; i < arguments.length; i++) {
            msg += arguments[i] + ', ';
        }
        alert(msg.substr(0, msg.length - 2));
    },
    queryStr: function (name) {   //取得QuertString
        var AllVars = window.location.search.substring(1);
        var Vars = AllVars.split("&");
        for (i = 0; i < Vars.length; i++) {
            var Var = Vars[i].split("=");
            if (Var[0] == name) return Var[1];
        }
        return "";
    },
    pathName: function () {
        return location.pathname.substring(location.pathname.lastIndexOf('/') + 1) + '/';
    }
};

這樣可以很明顯的看出來,  我們把所有類似功能的function通通都放在utility這個物件裡面

之後我們就可以在不同頁面上使用, 例如說

 


	alert( utility.queryStr('test')  );

 

感覺起來其實就跟C#類別的靜態方法一樣吧

 

這樣做的好處首先就是程式碼可以重複利用,  不用每頁都寫同樣的程式碼

而且好維護,  只要自己養起好習慣, 類似功能的程式都放到utility底下

 

要修改要增加其實都很方便

 

這邊其實可以順便講一下,  為什麼可以這樣宣告跟使用

JavaScript是一個非常活的語言,  不像.NET一樣嚴謹

 

對於物件而言,  什麼東西都可以是他的屬性,  包括function

所以我們宣告一個叫做utility的物件

 

在把許多的function指定為他的屬性

因此自然可以用{objName}.{attr}的方法去存取那些方法

 

所以在這邊可以看得出來,  JavaScript本身沒有什麼靜態類別這種東西

只是利用他本身的特性做出類似效果的東西而已

 

因此絕對沒有什麼制式或規定的寫法 (除了jQuery UI外, 之後有機會再聊)

充其量就是自己寫的順手,  不要給後人造成麻煩

 

其他就是工程師的基本功了,  如何在這樣的架構上平衡效能與功能

這些遇到再慢慢加強就好

 

=================================================================================================

二、擴充現有的類別

 

猶豫了一下,  決定還是把這個主題加進來

ASP.NET在3.5之後(?),  有一個好用的功能

 

我們可以在既有類別寫好之後,  幫他擴充新的功能進去

例如說

 


public static class Extension
{
    public static string Append(this string str, string szAppend)
    {
        return str + szAppend;
    }
}

我們可以用這個方法擴充既有的string類別

 

之後就可以在程式碼中使用


	string test = "abc";
test.Append("def");

 

可以看到string本身並沒有Append這個方法,  但是我們可以幫他擴充這個方法進去

而不用改寫原本的Class

 

 

為什麼這邊要提到這件事呢

 

JavaScript本身有提供一些現有的類別,例如Date, Number, Array之類的東西

而我們免不了要處理這些函數

 

 

例如說我們想要直接這個時間的月份,  而不想要每次取得月份還要手動+1

可能會寫一個function

 


	fucntion getRealMonth(var date) {
       return date.getMonth() + 1;
}

 

可是要是我們希望擴充原本的Date函式,  讓他能直接多一個getRealMonth()的功能

其實也是有辦法

 

就用JavaScript的prototype,  在global.js加入這段code


Date.prototype.getRealMonth = function () {
    return this.getMonth() + 1;
};

 

這段話的用意就是說,  把Date的prototype擴充一個方法叫做getRealMonth,  這樣之後我們可以這樣呼叫

 


	var date = new Date();
        alert(date.getRealMonth());

 

是不是變得直覺了許多

 

 

又回到問題,  為什麼能這樣寫

 

其實這個問題就建議去google一下什麼叫做prototype

這個東西其實就可以另外再開一個章節來詳述

 

為了避免半吊子的東西講不清楚

就不在這邊多提了

 

=================================================================================================

三、擴充現有的類別 (靜態方法)

 

要是說我們現在希望有一個功能叫做isDate,  判斷這個字串是不是時間格式

對專案來說,  這個類別跟Date有關,  我們應該要把他放在Date類別裡面

這樣假如要維護要修改比較好找位置

 

可是這種東西其實沒必要寫在prototype裡面

 

對我們而言他比較偏向是Date的靜態方法

 

那這要怎麼作?

 

 

其實就跟第一個主題一樣,  直接寫


Date.isDate = function (date) {
    return !isNaN(Date.parse(date));
};

這樣就可以直接下


	alert(  Date.isDate('2010/11/31')  );

 

坦白說這個主題其實跟主題一完全重複,  為什麼要再另外一個主題來講這個。

 

其實只是在重複一個觀念

 

JavaScript的object類別非常的活

他可以把任何的東西當成他的屬性,  也可以在程式碼的任何地方直接存取與操作屬性  ( 此點不完全盡然,  我們也是可以用特殊方法寫出private屬性,  之後的篇幅會再提到 )

 

而不管是Array, Number, Date  充其量也只是內建的比較特殊的object

但還是個object

 

因此我們也可以很自由的操作他的屬性

 

=================================================================================================

三、覆寫原有的方法

 

假如說現在希望把Date.getYaer()的功能給複寫,  希望getYear()的時候回傳的是民國的年份, 其實方法也很簡單

 


Date.prototype.getYear = function () {
        return '民國' + ( this.getFullYear() - 1911 ) + '年';
    };

 


	var date = new Date();
    alert(date.getYear());

這樣呼叫會發現本來應該要傳"2011"的值,  卻變成了"民國100年”

 

 

不過非常不鼓勵這樣做,  問題會非常多

 

只是既然不鼓勵,  為什麼這篇會提到

 

 

其實這只是在重複驗證一件事

我們可以在程式的任何地方

 

去操作物件的任何屬性

 

就算是JavaScript本身所定義好的getYear方法

也可以被我們用新的function給取代

 

 

其實對我而言,  這變成超可怕的事情

因為這表示說,  我寫的類別  裡面所有的屬性都變得不可靠

 

當他是public出去讓別人使用的時候

別人可以任意操作裡面的屬性

 

 

而在呼叫我寫好的方法的時候,  我本來要存取的屬性可能就會因此而不見

然後程式就包了 = =

 

我個人是有切身之痛   orz

 

 

在這邊最後可以提到, 

 

我們可以用 {objName}.{newAttr} = newValue; 去針對這個物件加一個新的屬性進去

也可以用 {objName}.property.{newAttr} = newValue; 去針對這個物件類別的所有new出來的物件加一個新的屬性進去

 

那我們假如想要把屬性給移除要怎麼作

 

{objName}.{newAttr} = undefined;  這樣就好

 

 

 

 

本來這邊想要寫怎麼新增一個類別

不知不覺講了這麼多還沒講到主題..只好等下一篇再說了  orz

 

--

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

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

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