顯示具有 JavaScript 標籤的文章。 顯示所有文章
顯示具有 JavaScript 標籤的文章。 顯示所有文章

Printing PDF File from ASP.NET

0 comments
使用 PDF(Portable Document Format)文件格式的好處在於,不論使用何種電腦平台或作業系統,都能忠實重現文件的原貌,也不會因為印表機的不同而影響排版。因此,PDF 格式在不同設備上輸出列印的一致性,非常適合用在印表機列印輸出的需求。

在網頁設計實務上,需要動態產生文件及講求排版格式的列印功能亦頗為常見。本文將示範如何使用 iTextShap 建立 PDF 文件,並加入 JavaScript 指令碼透過 Acrobat API 將文件自動輸出至指定的印表機。

下列程式碼範例會假設已經在 ASP.NET Web 網頁建立列印按鈕,並指定如下的 Click 事件處理常式。範例中的 PrintButton_Click 方法,會在指定的目錄下將建立含有列印指令碼的 PDF 文件,並透過泛型處理常式將文件傳送到用戶端,然後輸出至使用者的印表機。
protected void PrintButton_Click(object sender, EventArgs e)
{
Document document = new Document();
MemoryStream ms = new MemoryStream();
PdfWriter writer = PdfWriter.GetInstance(document, ms);
document.Open();

// 加入自動列印指令碼
writer.AddJavaScript(@"
var pp = this.getPrintParams();
pp.interactive = pp.constants.interactionLevel.silent;
pp.pageHandling = pp.constants.handling.none;
var fv = pp.constants.flagValues;
pp.flags |= fv.setPageSize;
pp.flags |= (fv.suppressCenter | fv.suppressRotate);
this.print(pp);
");

document.Add(new Paragraph("Testing Silent Printing with iTextSharp."));
document.Close();

Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "inline; filename=Report.pdf");
Response.BinaryWrite(ms.ToArray());
Response.End();
}

參考資料:
Silent Printing on Web based Java Application

繼續閱讀...

Using DHTML Tooltips with AJAX

0 comments
DHTML Tooltips 是一個跨瀏覽器相容的程式庫,提供豐富的功能讓你可以輕鬆建立、自訂多樣的工具提示(Tooltip)或是彈跳式訊息(Pup-up Box)。

程式庫提供 Tip 函式接受訊息字串參數,而 TagToTip 函式則允許你提供元素的識別名稱以顯示其 HTML 的內容。這兩個函式的用法都相當簡單,在此就不再贅述,本文將會側重在兩者的併用,並透過範例逐步說明告訴你,如何結合 jQuery 的應用來動態載入伺服器所提供的提示訊息。

在以下的範例網頁片段中,你會看到座位平面圖,以及其所組成映射區域,並且繫結要實作動態提示訊息的 onmouseover 事件處理函式。
<img src="Images/seatingplan.png" usemap="#seatingPlan" />
<map name="seatingPlan">
<area shape="rect" id="Front Stalls" coords="22,100,493,236"
onmouseover="showSectionInfo(this, 61);" />
<area shape="rect" id="Rear Stalls" coords="22,252,492,388"
onmouseover="showSectionInfo(this, 62);" />
</map>

在定義 onmouseover 事件處理函式之前,我們先在 jQuery 的 DOM ready 事件中,預先建立一個供資料讀取的過程中顯示處理訊息的隱藏區塊,並將 Tooltips 程式庫中的 UnTip 函式繫結所有映射區域元素的 onmouseout 事件處理常式。
$(document).ready(function() {
$('<div></div>')
.attr('id', 'ajaxLoading')
.html('Loading...')
.hide()
.appendTo('body');

$("map[name='seatingPlan'] *")
.mouseout(UnTip);
});

當映射區域元素 onmouseover 事件被觸發時,將會執行以下函式並將所接收的傳遞參數做為請求參數,來進行遠端呼叫:
function showSectionInfo(element, sectionId) {
if (data(element) === undefined) {
tooltip.call(element, 'ajaxLoading');
$.ajax({
url: 'InfoHandler.ashx',
type: 'GET',
data: {
id: sectionId
},
dataType: 'html',
error: function(xhr) {
UnTip();
alert('The ajax request failed.');
},
success: function(response) {
data(element, response);
tooltip.call(element);
}
});
} else {
tooltip.call(element);
}
}

在叫用 jQuery 的 ajax 函式處理非同步請求前,會先透過以下所定義的 tooltip 函式叫用 TagToTip 函式將先前所建立的隱藏訊息顯示出來,以回應使用者的請求:
function tooltip(elementId) {
var options = [ABOVE, true, SHADOW, true];
if (elementId === undefined) {
Tip.apply(this, $.merge([data(this)], options));
} else {
TagToTip.apply(this, $.merge([elementId], options));
}
}

當 AJAX 請求成功後,會透過如下的 data 函式將回應結果暫存到元素物件中,提供給後續可能的相同觸發動作讀取之用,以避免冗餘的資料請求。
function data(element, value) {
return value === undefined ? $.data(element, 'tooltip') :
$.data(element, 'tooltip', value);
}

最後,再次呼叫 tooltip 函式將回應結果委由 Tip 函式顯示出來。

繼續閱讀...

jQuery Floating Layer Plugin

3 comments
在許多網站設計中,浮動圖層(Floating Layer)經常被應用在較長的網頁文件中,以呈現能隨著瀏覽器的捲軸移動的浮動廣告或選單。

Floating Layer Plugin 是以 jQuery 原型物件(Prototype Object)為基礎所擴充的外掛程式庫。相較於先前版本,這次的版本除了將程式碼最佳化外,也增添了若干的新功能。

makeFloating( [options] )
透過元素選取(Selector)將特定的 DOM 元素封裝為具有 jQuery 函式功能的外覆物件後,你可以呼叫此函式來建立自訂的浮動圖層。函式可以接受傳遞物件參數來提供選項設定屬性,如以下所示:
屬性型別說明
positionobject指定 x 和 y 屬性來控制浮動區塊的定位方式。除了絕對座標來定位外,你也可以在 x 軸指定 "left"、"right"、"center" 決定水平位置,在 y 軸指定 "top"、"bottom"、"center" 來決定垂直位置。
範例:
  • {x:0, y:250}
  • {x:'right', y:'top'}
durationnumber指定動畫的顯示速度,以毫秒為單位。
easingstring指定變速移動的特效。
fixedboolean指定是否要固定圖層位置而不隨捲軸移動。

應用範例
首先,除了必要的 jQuery 程式庫外,你還需要下載本程式庫並引入到你的網頁中。然後,在網頁的文件的主體中加入區塊圖層,如以下範例:
<div id="floatlayer" style="
width: 50px;
height: 50px;
border: solid 1px #cccccc;
background-color: #d0d0ff;
z-index: 100;
display: none">
<!-- Place your content here -->
</div>

接下來,你可以處理 jQuery 的 DOM ready 事件,在網頁載入完成後,選取如上的圖層元素並叫用 makeFloating 函式:
$(document).ready(function() {
$('#floatlayer').makeFloating();
});

在預設的情況下,浮動圖層會配合網頁的可見區域以水平置左、垂直置中的方式定位,且會隨瀏覽器的捲軸移動產生擺動(Swing)的動畫效果。如果要變更浮動圖層的預設行為,你可以透過物件參數提供自訂的選項設定。如以下範例會建立水平置右、垂直置中,並加入線性(Linear)動畫效果的浮動圖層:
$('#floatlayer').makeFloating({
position: { x: 'right', y: 'center' },
easing: 'linear'
});

因為程式所表現的動畫效果是透過 jQuery 的 animate 函式來實現,所以 easing 參數只能支援內建的 linearswing 兩種特效。如果內建的動畫效果不能符合你的期望,建議你可以搭配 Easing Plugin 來擴增多種慢入(Easing In)和慢出(Easing Out)的動畫效果,如以下範例就使用了更為順暢的慢出特效:
$('#floatlayer').makeFloating({
position: { x: 'right', y: 'center' },
easing: 'easeOutBounce'
});

當然,如果你不需要動態移動的效果,你也可以透過物件參數的 fixed 屬性來固定圖層的位置:
$('#floatlayer').makeFloating({
position: { x: 'right', y: 'center' },
fixed: true
});

另外,程式庫還提供 floatingPosition 函式,讓你可以在建立浮動圖層物件後,重新定位浮動的目標位置,如以下範例:
$('#floatlayer').floatingPosition({ x: 'center', y: 'center' });


Download jquery.floatinglayer.zip

繼續閱讀...

在 Chrome 2.0 中為 JavaScript 偵錯

0 comments
和大多數人一樣,瀏覽器本身對我來說並不重要,它只是瀏覽網頁、使用網路應用的工具而已。相較於瀏覽器的功能性,快速、穩定的基本要素更能貼近我的需求。因此,強調簡潔、快速的 Google Chrome 瀏覽器,很快就在我心中佔據了一個無可取代的地位。

Chrome 不僅滿足使用者對瀏覽器的根本訴求,也從開發人員需求實務上的觀點設計許多的功能,希望能藉以協助開發人員提供更好的使用者經驗。在開發人員的工具中,最令我感興趣的,卻也是對我極度不友善而讓我望之卻步的,莫過於基於命令提示介面的 JavaScript 偵錯工具(JavaScript Debugger)了。當然,這對於既自詡為 Chrome 愛用者,又身兼網頁開發人員的我來說,似乎令人難以悅服。於是,最近我又再度興起要深入研究這個好用的小工具的念頭,如今在此分享自己的學習心得及蒐集的相關資訊,希望能對有興趣的朋友有所幫助。

命令
在 JavaScript 偵錯工具中,可使用的命令會依目前是否處於執行中或是中斷狀態而有所不同。在執行狀態中,也就是網頁尚未執行暫止於中斷點時的狀態下,可以使用的命令如下:
命令說明
b[reak] <function | script:function | script:line | script:line:pos> [condition]在來源中的位置或函式設定中斷點。
break_info [breakpoint #]

bi [breakpoint #]
顯示目前所有中斷點資訊,或是已指定中斷點的資訊。
clear <breakpoint #>移除已指定的中斷點。
h[elp] [command]顯示所有命令的描述,或是已指定命令的詳細描述。
p[rint] <expression>評估運算式並輸出執行結果。
scripts顯示所有可偵錯的指令碼資訊。
註:[] 表示可省略或選擇性項目; <> 表示必須提供的項目。

當網頁暫止於中斷點的狀態時,可以使用的命令如下:
命令說明
args顯示目前函數的參數。
b[reak] [<function | script:function | script:line | script:line:pos> [condition]]在來源中的位置或函式設定中斷點,或是不使用任何參數停止偵錯並終止程式執行。
break_info [breakpoint #]

bi [breakpoint #]
顯示目前所有中斷點資訊,或是已指定中斷點的資訊。
backtrace [from frame #] [to frame #]

bt [from frame #] [to frame #]
顯示目前函式的呼叫堆疊(Call Stack)。
clear <breakpoint #>移除已指定的中斷點。
c[ontinue]繼續執行到下一個中斷點。
dir <expression>顯示物件的詳細資訊。
f[rame] <frame #>顯示目前堆疊框架(Stack Frame)的偵錯資訊,或是已指定堆疊框架的偵錯資訊。
h[elp] [command]

? [command]
顯示所有命令的描述,或是已指定命令的詳細描述。
locals顯示目前堆疊框架中所有變數的值。
n[ext]逐步執行每行指令碼,如果是執行函式呼叫,則會進入函式內的第一行指令碼。
p[rint] <expression>評估運算式並輸出執行結果。
scripts顯示所有可偵錯的指令碼資訊。
source [from line] | [<from line> <num lines>]

ls [from line] | [<from line> <num lines>]
顯示目前堆疊框架的原始碼,或從指定的行號開始顯示。
s[tep]逐步執行每行指令碼,如果是執行函式呼叫,則不會進入函式直接執行。
stepout

so
在目前的函式中,繼續執行指令碼直到函式返回,然後在呼叫函式的返回點中斷。

範例網頁
為了幫助你更快熟悉偵錯命令的應用,將會利用以下範例網頁,用逐步解說的的方式進行偵錯示範。
<html>
<head>
<title>Sample Page</title>
<script type="text/javascript" src="shape.js"></script>
<script type="text/javascript">
function Rectangle(x, y, w, h, cnv) {
Shape.call(this, x, y);
this.width = w;
this.height = h;
this.convas = cnv;
return true;
}

Rectangle.prototype = new Shape();
Rectangle.prototype.draw = function() {
if(this.convas) {
this.convas.innerHTML += "Drawing a Rectangle at:" + this.getCoordinates() +
", width " + this.width + ", height " + this.height + "<br/>";
}
};

function drawRectangle() {
var x = parseInt(document.getElementById("x").value);
var y = parseInt(document.getElementById("y").value);
var w = parseInt(document.getElementById("w").value);
var h = parseInt(document.getElementById("h").value);
var cnv = document.getElementById("cnv");
var rect = new Rectangle(x, y, w, h, cnv);
rect.draw();
}
</script>
</head>
<body>
x:<input id="x" type="text" size="3" /> y:<input id="y" type="text" size="3" />
width:<input id="w" type="text" size="3" /> height:<input id="h" type="text" size="3" />
<input type="button" value="Draw Rectangle" onclick="javascript:drawRectangle();" />
<div id="cnv"></div>
</body>
</html>

在範例網頁中引入一個外部的 js 檔案,其內容如下:
function Shape(x, y) {
this.x = x;
this.y = y;
return true;
}

Shape.prototype = {
getCoordinates : function() {
return "(" + this.x + " ," + this.y + ")";
}
};

當你在 Chrome 中開啟範例網頁後,請按一下 [網頁功能] 功能表,然後在 [開發人員選項] 點選 [為 JavaScript 偵錯] 或是使用鍵盤捷徑 [Ctrl+Shift+L],這時 JavaScript 偵錯工具便會開啟並附加在作用中的分頁。另外,你還需要 [檢視網頁原始碼],除了便於在偵錯期間檢視程式碼外,也可以利用原始碼的行號來設定中斷點。


偵錯逐步解說
在進行偵錯前,我們可以在命令列輸入 "scripts" 來檢視附加在範例網頁的指令碼資訊,如以下輸出結果:
$ scripts
file:///C:/shape.js (lines 11)
file:///C:/test.htm (lines 4-30)
[unnamed] (source:"javascript:void(0)")

如果你要將中斷點設在外部檔案中的 Shape 建構函式,你可以執行如下的命令:
$ break Shape
set breakpoint #1

除了指定函式中斷點外,你也可以指定原始碼的位置來設定中斷點。例如,若要將中斷點設在如下的指令碼行:
var rect = new Rectangle(x, y, w, h, cnv);

那麼,你可以先從 [檢視網頁原始碼] 中得知指令碼行所在行號,然後執行如下的命令:
$ break file:///C:/test.htm:28
set breakpoint #2

然而,上述設定中斷點的方式都屬於無條件中斷,事實上,你還可以選擇性地設定需同時符合特定條件的中斷點:
$ break file:///C:/test.htm:24 x<0
set breakpoint #3

這個命令將會在以下的原始碼位置設定中斷點,且變數 x 的值必須滿足特定條件才會暫止程式執行。
var y = parseInt(document.getElementById("y").value);

當你完成如上的中斷點設定後,便可以使用 "break_info" 檢視所有的中斷點資訊:
$ break_info
Num breakpoints: 3
id=1, hit_count=0, type=function, target=Shape
id=2, hit_count=0, type=script, target=file:///C:/test.htm, line=27
id=3, hit_count=0, type=script, target=file:///C:/test.htm, line=23, condition=x<0

現在,你可以回到範例網頁的頁籤,然後填入必要欄位並按下按紐執行指令碼。如果你在 x 欄位輸入的數值小於零的話,那麼程式就會暫止在第三個中斷點:
paused at breakpoint 3: drawRectangle(), file:///C:/test.htm
24: var y = parseInt(document.getElementById("y").value);

若要繼續執行指令碼,可以視情況選擇使用 "step" 或 "next" 命令來逐行執行指令碼:
$ next
25: var w = parseInt(document.getElementById("w").value);

或是,使用 "continue" 命令繼續執行到下一個中斷點,也就是之前所設定的第二個中斷點:
$ continue
paused at breakpoint 2: drawRectangle(), file:///C:/test.htm
28: var rect = new Rectangle(x, y, w, h, cnv);

當程式中斷執行時,可以輸入 "locals" 命令來檢視目前執行函數中的所有變數值:
$ locals
w = 400
cnv = #<an HTMLDivElement>
x = -10
y = 0
h = 600
rect = undefined

若程式繼續執行,將會暫止在第一個中斷點:
$ continue
paused at breakpoint 1: #.Shape(x=-10, y=0), file:///C:/shape.js
2: this.x = x;

在偵錯過程中,你可以選擇性地切換堆疊框架來進行偵錯。首先,你可以透過 "backtrace" 命令來檢視所有堆疊框架的資訊:
$ backtrace
Frames #0 to #4 of 5:
#00 #<an Object>.Shape(x=-10, y=0) file:///C:/shape.js line 2 column 12 (position 36)
#01 new Rectangle(x=-10, y=0, w=400, h=300, cnv=#<an HTMLDivElement>) file:///C:/test.htm line 7 column 15 (position 58)
#02 drawRectangle() file:///C:/test.htm line 28 column 20 (position 839)
#03 #<an HTMLInputElement>.[anonymous](evt=#<a MouseEvent>) file:///C:/test.htm line 37 column 12 (position 177)
#04 #<an HTMLInputElement>.onclick(evt=#<a MouseEvent>) file:///C:/test.htm line 38 column 4 (position 197)

然後,再使用 "frame" 的命令來切換到指定的堆疊框架,如以下所示:
$ frame 1
#01 Rectangle, undefined
7: Shape.call(this, x, y);

你可以視需要使用 "source" 命令來檢視目前堆疊框架的原始碼:
$ source
5:
6: function Rectangle(x, y, w, h, cnv) {
>>>> Shape.call(this, x, y);
8: this.width = w;
9: this.height = h;
10: this.convas = cnv;
11: return true;
12: }

如果不打算繼續執行指令碼,請使用 "break" 命令停止偵錯並終止程式執行:
$ break
JavaScript execution already stopped.


參考資料:
Sample debug session with Google Chrome JavaScript debuger
Basic information on Chrome's Debugger
Google Chrome JavaScriptデバッガ完全マニュアル

繼續閱讀...

Mooquee - Marquee with Mootools

2 comments
Mooquee 是一個以 MooTools 為基礎所設計的跑馬燈的 JavaScript 類別,它提供建構選項可以表現出多種不同於 Marquee 標籤的跑馬燈動畫效果。

實作時,除了要引用 Mooquee 程式庫外,你還需要搭配 MooTools 1.2。在建構 Mooquee 物件時,你可以設定以下建構選項:
  • element:指定做為跑馬燈區塊的 HTML 元素識別碼。
  • cssitem:指定播放項目的樣式類別名稱。
  • firstitem:指定第一個播放項目的索引。
  • direction:以 up、down、left 或 right 指定跑馬燈移動的方向。
  • pause:以秒為單位指定每次停止時間,且必須大於或等於 duration 的值,其預設值為 1 秒。
  • duration:以秒為單位指定每次移動所需時間,其預設值為 1 秒。
  • overflow:設定當內容超出跑馬燈區塊範圍時的處理方式,預設值為 hidden。
  • startOnLoad:指定是否自動播放,預設值為 true。
如以下範例會建立向上垂直捲動的跑馬燈:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Mooquee Example</title>
<style type="text/css">
#mooquee_container {
width: 200px;
height: 20px;
border: 1px solid;
}
.mooquee_item {
background-color: #ffffff;
}
</style>
<script type="text/javascript" src="mootools-1.2-core.js"></script>
<script type="text/javascript" src="Mooquee.js"></script>
<script type="text/javascript">
window.addEvent('domready', function() {
var mooquee = new Mooquee({
element:'mooquee_container',
cssitem:'mooquee_item',
direction:'up',
duration:1,
pause:2,
firstitem:0
});
});
</script>
</head>
<body>
<div id="mooquee_container">
<div class="mooquee_item">跑馬燈訊息第 1 則</div>
<div class="mooquee_item">跑馬燈訊息第 2 則</div>
<div class="mooquee_item">跑馬燈訊息第 3 則</div>
</div>
</body>
</html>

繼續閱讀...

Slideshow 2!

0 comments
Slideshow 2! 是一個以 Mootools 為基礎所設計的 JavaScript 類別,它可以協助你輕易建立多種有如 Flash 動畫效果般的 DHTML 投影片播放器。除了完整的功能之外,其動感的視覺效果表現更是 Slideshow 2! 的重點特色。你可以從這裡找到最新版本的原始碼及說明文件。

在這裡,我就列舉兩種最常用的播放器型態。如以下範例會以淡入淡出(Fading)的特效,循環播放含有標題的相片:
<html>
<head>
<title>Slideshow 2! Example</title>
<link rel="stylesheet" type="text/css" href="css/slideshow.css" media="screen" />
<script type="text/javascript" src="js/mootools.js"></script>
<script type="text/javascript" src="js/slideshow.js"></script>
<script type="text/javascript">
window.addEvent('domready', function() {
var data = {
'1.jpg': { caption: '晨曦乍現' },
'2.jpg': { caption: '藍天浮雲' },
'3.jpg': { caption: '夕陽餘暉' }
};
var myShow = new Slideshow('show', data, {
captions: true, controller: true, hu: 'images/', width: 400, height: 300
});
});
</script>
</head>
<body>
<div id="show" class="slideshow">
<img src="images/1.jpg" width="400" height="300" />
</div>
</body>
</html>



你也可以加入有縮圖的移動捲軸:
<script type="text/javascript">
window.addEvent('domready', function(){
var data = ['1.jpg', '2.jpg', '3.jpg', '4.jpg', '5.jpg', '6.jpg', '7.jpg'];
var myShow = new Slideshow('show', data, {
thumbnails: true, controller: true, hu: 'images/', width: 400, height: 300
});
});
</script>


除此之外,它還能表現出遠近縮放(Zoom)、推進(Push)等特效的動態相片。你可以從這裡看到更多令人驚艷的範例作品展示。如果你覺得這個程式真是受用,不妨也可以到作者的網站,用贊助的行動鼓勵作者喔!

資料來源:
Slideshow 2! A javascript class for Mootools 1.2 to stream and animate the presentation of images on your website by Aeron Glemann

P.S. 本文所使用的範例圖片係取材自 http://www.freeimages.co.uk/

繼續閱讀...

認識 JavaScript 的物件導向技術

1 comments
如果要說 JavaScript全世界被誤解最深的程式語言,其實一點也不為過。長久以來,它的名稱讓許多人誤以為是 Java 的子集合。當初 JavaScript 是以作為給非程式人員的腳本語言為訴求,所以學習門檻低,很容易就能上手。正因為如此,它也讓許多人停留在粗糙、簡單的既定印象,即便到現在已經進化成一個物件導向程式語言(Object-Oriented Programming Language, OOPL)。

現在,物件導向程式設計(OOP)是已普遍被應用到許多 JavaScript 程式庫(如 AJAX Library)。如果,你要使用這些程式庫,你就有必要深入理解 JavaScript 語言的概念,才能靈活運用它來應付更複雜的 Web 應用程式。

然而 JavaScript 對 OOP 的支援方式,卻與其他以類別為基礎(Class-Based)的物件導向語言大相逕庭。本文接下來,將帶你初探 JavaScript 語言對 OOP 的支援能力。

物件
JavaScript 物件是索引鍵和值配對(Name-Value Pair)的集合。「名稱」的部分必須是字串,而「數值」可以是任何資料型別。它的資料結構跟 C# 中的 Dictionary 和 Hashtable 非常相近。

以下是建立物件的基本方法:
var rectangle = new Object();
rectangle.width = 5;
rectangle.height = 3;
rectangle.getArea = function() {
return width * height;
}
alert(rectangle.getArea()); // Displays "15"

以上範例程式碼同時呼叫 new 和建構函式,會建立新的 Object 物件。然後,才指派屬性及方法給物件變數,而所謂的方法,其實也只是參考到 Function 物件的屬性而已。這個範例顯示,JavaScript 物件的屬性不一定要事先宣告,你可以在任何時候加入額外的屬性。如果你把 JavaScript 物件它當成是在 Dictionary 物件,將指派屬性視同是加入索引鍵和值,應該就不難理解了。

你也可以使用 JavaScript 1.2 版本支援的物件實體語法(Object Literal),來宣告及初始化物件:
var rectangle = {
"width" : 5,
"height" : 3,
"getArea" : function() {
return width * height;
}
};
alert(rectangle.getArea()); // Displays "15"

每個屬性初始設定以逗號分隔的定義方式,與 C# 3.0 的物件初始設定式非常類似,唯一的差別在於這裡只接受字串索引鍵。

當要存取物件的屬性時,你可以使用熟悉的 "." (點)運算子來存取。
rectangle.width = 5;
var width = rectangle.width;

或者,使用 "[]" 運算子來取得及設定物件的屬性。
rectangle["width"] = 5;
var width = rectangle["width"];

當然,你也可以在任何時候移除物件的屬性:
delete rectangle["width"];
delete rectangle.height;

函數也是物件
JavaScript 函式其實就是包含可執行程式碼的 Function 物件。它被視為第一級物件(First-Class Object),這意味它可以被動態建立,儲存在變數、陣列和物件中,也能做為函式的「傳入參數」或「傳回結果」。
function add(x, y) {
return x + y;
}
alert(add(1, 2)); // Displays "3"

當你使用 function 關鍵字宣告函式,其實就是在定義一個 Function 物件。事實上,在執行時期會配置與函數同名的 Function 物件。以上面的範例來說,會建立一個名為 add 的函數物件。所以,你才可以在函數物件變數使用 "()" 運算子呼叫函式程式碼。

除了具名函數外,你也可以宣告匿名(Anonymous)函數,並傳回它的參照:
var add = function(x, y) {
return x + y;
};
alert(add(1, 2)); // Displays "3"

當然,你也可以在宣告的同時,直接呼叫匿名函數,並取得傳回值:
var result = (function(x, y) {
return x + y;
})(1, 2);
alert(result); // Displays "3"

另外,你也可以使用 Function 建構函式建立函數物件:
var add = new Function("x", "y", "return x + y;");
alert(add(1, 2)); // Displays "3"

Function 函數比較特殊,即使沒有使用 new 關鍵字也會產生相同的結果。

在匿名函式中,如果你要在函式內部遞迴呼叫自己,可以使用 arguments.callee 的屬性。當函式被呼叫時,會自動建立包含所有傳遞參數值的陣列物件,並指派給名為 arguments 的區域變數。該物件提供了一個叫做 arguments.callee 的屬性,這個屬性會指向的目前的函式,因此可以用來做遞迴呼叫:
var fso = new ActiveXObject("Scripting.FileSystemObject");
(function(folder) {
var files = new Enumerator(folder.Files);
while(!files.atEnd()) {
document.write(fso.BuildPath(folder.path, files.item().name));
files.moveNext();
}
var subfolders = new Enumerator(folder.SubFolders);
while(!subfolders.atEnd()) {
arguments.callee(subfolders.item());
subfolders.moveNext();
}
})(fso.GetFolder("d:\\"));

以上範例,使用遞迴方式列舉根目錄下的所有子目錄及檔案。

自訂物件
通常,我們所說的物件是指是類別或結構的實體。然而,JavaScript 只有建構函式,卻沒有類別。所謂的建構函式,其實就是一般的函式。最接近類別的方式,是定義如下的建構函式:
function Shape(x, y) {
this.x = x;
this.y = y;
this.getCoordinates = function(){
return "(" + this.x + " ," + this.y + ")";
}
}
var objShape = new Shape(5, 10);
alert(objShape.getCoordinates()); // Displays "(5, 10)"

上例中的 x 、y 是屬性成員,getCoordinates() 是方法成員。在這裡第一次出現尚未提過的 "this" 關鍵字,稍後會有詳細說明。請先看這行程式碼:
var objShape = new Shape(5, 10);

當你使用 "new" 運算子呼叫建構函式時,JavaScript Engine 會執行以下動作:
  • 建立新的 Object 物件。
  • 在 Shape 函式的執行環境中,將所建立的物件參考指派給 this 值。
  • 接著,將此物件的 constructor 屬性指向 Shape 函數,以及將其內部隱含的 __proto__ 屬性指向 Shape 函數物件的 prototype 屬性。
  • 然後,執行 Shape 函式內的程式碼。
  • 最後,傳回物件參考。

請記得,除了 Function 函式外,在一般情況下,只要使用 new 運算子呼叫函式,就會傳回完全初始化的 Object 物件。

JavaScript 的程式碼都是在執行環境(Execution Context)中執行,執行環境包含變數範圍鏈(Scope Chain)的資訊以及呼叫此方法的物件參照(也就是 this 值)。而可執行程式碼又分為 Global Code 、 Function Code 及 Eval Code 三種。不同種類的程式碼,會建立不同的執行環境,而 this 值需視執行環境或呼叫者而定:
  • Global Code
    任何在函式以外(不屬於函式的一部分)的程式碼。只會產生一個 Global 執行環境。這時的 this 值就會是 Global 物件(在瀏覽器中即為 window 物件)。
  • Function Code
    宣告於函式本體的程式碼。每次的函式呼叫都會建立個別的執行環境。如果函式是透過物件參考來呼叫,那麼 this 值就會被設定為該物件參考。若是使用 new 運算子呼叫函式,則 this 值會參考到新產生的 Object 物件。如果是透過函式物件的 call() 或是 apply() 方法來呼叫,則需視傳入的第一個參數而定。
  • Eval Code
    透過 eval() 函式執行的程式碼。其 this 值與所在的執行環境的 this 值相同。

原型繼承
原型物件(Prototype)是 JavaScript 用來模擬類別階層(Class Hierarchy)的中心概念物件。每個函式物件(fucntion 型別的物件)都有一個 prototype 屬性(該物件所擁有的原型物件),它可以用來加入自訂屬性及方法。所謂的原型物件,其實也是使用 Object 建構函式所建立的物件。另外,所有物件實體內部還隱含一個名為 __proto__ 的屬性,它會指向其建構函式的 prototype 屬性。當然,建構函式的 prototype 屬性參照的原型物件也隱含有 __proto__ 屬性,指向它的建構函式的原型物件,以此類推,最後追朔到最終基底原型 Object.prototype 為止。每一個物件都會繼承一整鏈的原型,這樣的鏈結關係稱之為原型鍊(Prototype Chain)。

我們都知道 Object 的原型物件擁有以下的屬性成員:
  • constructor
  • toString()
  • toLocaleString()
  • valueOf()
  • hasOwnProperty(propertyName)
  • isPrototypeOf(objectRef)
  • isPropertyEnumerable(propertyName)

當你建立自訂物件時,將繼承 Object 物件原型的所有屬性和方法。
function MyClass() {

}
var myObject = new MyClass();
alert(myObject.toString()); // Displays "[object Object]"

上例中,我們透過自訂的物件參照呼叫 toString() 方法。事實上,這個方法是來自 Object 的原型物件。那麼 JavaScrpt 是如何解析呢?當你試圖要存取物件的屬性/方法時,沒有定義在物件中,那麼 JavaScript 就會檢查該物件的原型。如果還是沒有,就會循我們前面所提到的原型鏈往上尋找,直到 Object.prototype 為止。

現在,你已暸解 JavaScript 是如何以原型鏈來模擬類別階層關係。接下來,我們就可以利用原型物件來實做衍生類別:
function Shape(x, y) {
this.x = x;
this.y = y;
}
Shape.prototype = {
getCoordinates : function() {
return "(" + this.x + " ," + this.y + ")";
}
};
function Rectangle(x, y, width, height) {
Shape.call(this, x, y);
this.width = width;
this.height = height;
}
Rectangle.prototype = new Shape();
Rectangle.prototype.getArea = function() {
return this.width * this.height;
};
var objRect = new Rectangle(100, 200, 5, 10);
alert(objRect.getCoordinates()); // Displays "(100, 200)"
alert(objRect.getArea()); // Displays "50"

在上例中,Shape 可視為基底類別(Base Class),而 Rectangle 是衍生類別(Derived Class)。其中,繼承的關鍵在於設定 Rectangle.prototype 屬性,將 Shape 物件加入 Rectangle 的原型鏈。另外,在 Rectangle 建構函式中,透過函數物件的 call() 方法,呼叫 Shape 建構式來進行初始化。

結語
為什麼要使用 OOP ?說穿了,就是為了能夠讓程式設計更接近人類的自然思維,讓程式碼易於擴充與維護。 當你使用 OOPL 撰寫程式碼時,你就已經是在 "OOP" 了。正所謂「只在此山中,雲深不知處」,差別只在於你對物件導向是否有所體認罷了。誠如 Kenming 在「不要從程式語言學習「物件導向」!」一文中提到:

因為,物件導向是一種思維,是一種哲理,是一種典範,甚至是一種生活觀,你需要綜合相當多的知識,蘊化為 "智慧",來協助你如何應付與應對軟體的 "善變",並能提供具體的解決方案。

雖然 JavaScript 對 OOP 可提供很好的支援,但對習慣類別型程式設計的人來說,或許會有很大的落差。所幸,未來 JavaScript 2.0 版本將朝向類別型語言發展。不過,在此之前如果你希望使用 JavaScript 撰寫更具彈性的物件導向程式,可以參考 MooTools 所提供的程式庫,它提供了絕佳的類別函式,讓你可以更容易撰寫類別式的 JavaScript 程式碼。

參考資料:
使用物件導向技術來建立進階 Web 應用程式
Javascript Closures

繼續閱讀...

為你的 Blogger 加入書籤按鈕

14 comments
現在有許多好用的外掛程式可以幫您的 Blogger 加上多個書籤按鈕,尤其像是 AddThisShareThis,更是集合各種社交書籤,且功能更為完整。可惜的是這些好用的外掛程式普遍都沒有支援國內的書籤網站(例如 Hemidemi、My Share 等),有感於此,於是自己才著手寫了這個支援國內書籤按鈕的外掛程式,而且只需要幾個簡單的步驟及少量的 DHTML 程式碼即可完成安裝。

接下來將以 Blogger 新的網頁範本格式(XML)來說明安裝步驟:

1. 進入 Blogger 的管理介面,在[版面配置]點選[修改HTML],並勾選[展開小裝置範本]。

2. 在 <b:skin> 區段中加入以下 CSS 代碼:
.social-button { 
border: 0;
padding: 1px;
width: 16px;
height: 16px;
opacity: .5;
-moz-opacity: .5;
filter: alpha(opacity=50);
}
.social-button:hover {
opacity: 1;
-moz-opacity: 1;
filter: alpha(opacity=100);
}

3. 找到如下的標籤代碼:
<p><data:post.body/></p>

並在其後加入以下程式碼:
<!-- Bookmark Button BEGIN -->
<div align='right'>
<script type='text/javascript'>
_bookmarkTitle = &#39;<data:post.title/>&#39;;
_bookmarkUrl = &#39;<data:post.url/>&#39;;
</script>
<script src='http://renjin.liu.googlepages.com/social_widget.js' type='text/javascript'/>
</div>
<!-- Bookmark Button END -->

4. 儲存範本、測試。



 Download Source Code

繼續閱讀...