:::

NetBeans的JavaScript編輯中呼叫自動完成功能(Code Completing)

NetBeans的JavaScript編輯中呼叫自動完成功能(Code Completing)

image

NetBeans是用來撰寫網頁程式語言的整合開發環境(IDE)。他的特色之一就是程式碼的自動完成提示(Smart Code Completion),這也是我認為IDE應該要有的基本功能之一。然而NetBeans在撰寫JavaScript的時候,除非手動打開自動完成功能,不然只有在輸入觸發關鍵字「.」的時候才會顯示自動完成提示。也就是說,像我輸入常用的字詞,例如「var」、「new」、「function」等關鍵字的時候,他根本就不會出現提示,然後我就會很手殘地老是把「function」打成「funciton」,程式出錯跑不出來。

為了要在輸入一般文字時就能呼叫自動完成功能,我需要使用NetBeans的快捷鍵Ctrl+Space,然而他卻很不幸地跟Windows的切換輸入法相衝。因此又得修改快捷鍵設定,才能順利地呼叫NetBeans的自動完成功能。

以下介紹中,我使用的是NetBeans 6.9版本,作業系統是Windows 7 64-bit。但不管哪個版本,應該大致上都大同小異。

預設呼叫自動完成的快捷鍵 Ctrl + Space

image

要呼叫自動完成的快捷鍵(Hotkey),預設是使用「Ctrl + Space」。同樣身為中文Windows使用者的您看到這邊一定是滿臉陰影,因為Ctrl + Space是Windows用來切換輸入法的快捷鍵,在NetBeans中根本就沒辦法用Ctrl + Space來呼叫。

NetBeans的FAQ裡面也有提到這個問題,而他建議去設定Keymap來修改這個快捷建設定。接下來我們也照著他的方法來修改NetBeans呼叫自動完成的快捷鍵吧。

修改快捷鍵設定為 Alt + Z

基於Aptana StudioSpket IDE等我常用的IDE的習慣,通常呼叫自動完成的快捷鍵是設定在Alt + Z,這個按鍵也非常容易使用,因此以下我介紹如何將NetBeans的自動完成快捷鍵設定為Alt + Z。

  1. 開啟Tools中的Options。
    image 
  2. 切換到Keymap分頁,並在Search in Shortcuts欄位中輸入空白鍵,你就可以找到「Show Code Completion Popup」,而他後面的Shortcut(快捷鍵設定)則是「Ctrl+SPACE」
    image
  3. 雙擊要修改的快捷鍵設定,並按下「Alt」跟「Z」,並按下「Enter」確定。按下Enter之後列表會跳掉,但是再回去Searhc Alt+Z的時候,就會看到修改已經完成:
    image 
  4. 按「Ok」按鈕完成設定,然後在寫JavaScript的時候就開心地使用Alt + Z來呼叫自動完成吧。
    image


結語

在尚未得知這個方法之前,我一直以為NetBeans的自動完成非常地彆腳。我喜歡Aptana那種輸入第一個字就會立刻帶出自動完成的設定,而至今我還沒有在其他JavaScript IDE看到能在這點與他相比的特性。然而退而求其次,在這種按「Alt + Z」才能呼叫自動完成的IDE中雖然需要多一個這樣的步驟,但習慣了也勉強可以接受。

NetBeans的自動完成提示最貼心的地方在於會將重要的屬性與方法往上提,而不是像Aptana把所有的名稱都依照字母排序,只是幫你找出符合開頭的文字。

image

然而遺憾的是,NetBeans的JavaScript分析器不如Aptana來得聰明。而且他說是支援JSDoc,可是又不是很標準的JSDoc語法。像是JSDoc網站上認定的繼承語法「@extends」在Aptana跟Spket IDE都可以使用,到了NetBeans當中就只能認得「@inherits」。

因此到了最後,我還是繼續用Aptana來開發JavaScript。真有種繞了一大圈之後又回來原地的感覺。

(more...)

談JavaScript使用prototype實作物件導向的探討

談JavaScript使用prototype實作物件導向的探討

image

JavaScript雖然是物件導向的程式語言,但是他的物件導向方法並不統一,跟我們在學校所學的C、Java或甚至是PHP等都有很大的差異。

最知名的JavaScript繼承方法之一是使用「prototype」(此處並不是在講Prototype的JavaScript Framework喔),Fillano(馮旭平)談論物件導向Javascript - 實作繼承的效果為prototype的繼承方法作了很多說明,網路上也有很多教學是以「prototype」方式進行,例如JSDoc在介紹使用方法中就有示範一段以prototype作為繼承的例子。

我在論文系統發展初期也是使用prototype來實作繼承,但是做到一半的時候,我發現prototype其實是有很多問題的。除了老是要撰寫「this.prototype.」的宣告開頭讓程式大小居高不下,prototype還有使用「傳址」(連結位址) 而非「傳值」(複製資料) 來初始化變數的缺點,這會導致繼承時上層類別的變數會因為傳址而被變更的問題。

這一篇就是要來談談JavaScript中以prototype實作繼承的這個問題,並且一一探討解決的方法。


prototype實作繼承的方法與問題

prototype是JavaScript物件中特殊的一種屬性,透過指定prototype屬性,便可以指定要繼承的目標。然而當類別的屬性為「物件」的資料型態(例如有個屬性為birthday物件,而該物件包含了year、month、day這三種屬性),而方法本身又是修改該屬性中的屬性(例如修改birthday中的year這個屬性)時,就會影響到上層物件的屬性內容。

以下我舉一個簡單的範例:「Ancestor」上層類別跟「Child」子類別。首先下圖是他的UML類別圖:

image

將上面的類別圖寫成JavaScript物件導向的繼承,就會變成下列的程式碼:

//上層類別
function Ancestor() {
    this.setBirthdayYear(1889);
}

//屬性
Ancestor.prototype.birthday = {
    year: null,
    month: null,
    day: null
};

//方法
Ancestor.prototype.setBirthdayYear = function (year)
{
    this.birthday.year = year;
}; 

//子類別
function Child() {
    //即使Child沒有宣告setBirthdayYear的方法,
    //也可以使用上層類別Ancestor的setBirthdayYear
    this.setBirthdayYear(1915);  
}

//Child繼承Ancestor
Child.prototype = new Ancestor();   

最重要的繼承方法是最後一行「Child.prototype = new Ancestor();」,透過此宣告,Child類別就能使用上層類別Ancestor的屬性birthday跟方法setBirthdayYear()。

乍看之下這樣是沒有問題的,但是實際上Child在執行setBirthdayYear時,卻會連帶地影響到Ancestor的birthday屬性。舉例來說:

var ancestor = new Ancestor;
var child = new Child;
document.write(ancestor.birthday.year);    //應該要顯示1889,但卻顯示1915
document.write(child.birthday.year);    //顯示1915

很遺憾地,Ancestor的birthday屬性的確被Child修改了。由此可知,這種JavaScript物件導向的實作方式有著傳址運作的缺陷。

改進prototype的實作方式:在constructor指定參數

要改善上述實作方式的方法之一,是在constructor(建構子)的時候指定參數,讓每次類別在實體化成為物件的時候,都去重新指定參數的內容。以下是修改之後的Ancestor類別:

//上層類別
function Ancestor() {
    //初始化屬性
    this.birthday = {
        year: null,
        month: null,
        day: null
    };
    
    this.setBirthdayYear(1889);
}

在建構子當中加入屬性初始化的設定之後,就算之後Child修改了Ancestor的屬性,因為Ancestor在實體化的時候每次都會將屬性初始化,所以Ancestor看起來就像是不會受到Child影響一樣。

值得注意的是,這只有在屬性為物件時才可能會受到影響,請對可能受到影響的屬性物件進行初始化,如果屬性的資料型態為字串、數字、布林值,那麼即使不進行初始化也是無所謂的。相關的探討請見JavaScript中參數的傳值與傳址探討

這樣做是正確的,但在多重繼承的時候,仍會有一些問題發生。請繼續看以下的探討。

多層繼承時將會導致問題發生

儘管上一節中為類別的建構子加入初始化屬性的設定,就可以暫時解決上層類別受到影響的問題,但是如果要實作多層繼承的時候,卻仍因為最下層類別不會去執行最上層類別的建構子,導致最後仍沒有進行初始化屬性的錯誤。

接下來我再舉一個例子作為說明,以下是多層繼承的UML類別圖:(雖然這並不是一個很標準的物件導向的例子,大家請不要見怪。)

image

現在我們有4個類別,各別是Grandfather爺爺、Father爸爸、Son兒子、Daughter女兒。他們用上述的prototype實作方式來寫成JavaScript程式碼之後,會如以下:

//最上層類別
function Grandfather() {
    this.birthday = {
        year: null,
        month: null,
        day: null
    };
    this.setBirthdayYear(1889);
}

//屬性
Grandfather.prototype.birthday = {
    year: null,
    month: null,
    day: null
};

//方法
Grandfather.prototype.setBirthdayYear = function (year)
{
    this.birthday.year = year;
}; 

//上層類別
function Father() {
    this.setBirthdayYear(1915);    
}

//Father繼承Grandfather
Father.prototype = new Grandfather();    

//子類別之一
function Son() {
    this.setBirthdayYear(1943);    
}

//Son繼承Father
Son.prototype = new Father();    

//子類別之二
function Daughter() {
    this.setBirthdayYear(1945);    
}

//Daughter繼承Father
Daughter.prototype = new Father();

類別名稱修改,並且加入了Son跟Daughter這兩個類別之外,基本上跟前面舉例中的Ancestor與Child沒有太大差別。但是這樣子執行的時候,卻會發生很大的問題:

var grandfather = new Grandfather();
var father = new Father();
var son = new Son();
var daughter = new Daughter();

document.write(grandfather.birthday.year);    //顯示1889
document.write(father.birthday.year);    //應該要顯示1915,但卻顯示1945
document.write(son.birthday.year);    //應該要顯示1943,但卻顯示1945
document.write(daughter.birthday.year);    //顯示1945

你可以注意到,Daughter修改了屬性的資料,連帶影響到了Father跟Son,也就是說,除了最上層Grandfather因為有在建構子時初始化屬性之外,其他沒有在建構子中初始化屬性的子類別仍會受到影響。

你可能想到說:那麼同樣地也在Father、Son、Daughter時初始化屬性不就得了?但這樣的作法是與物件導向中為了不要重複撰寫程式碼的原則相違背,因此並不推薦這麼做。

解決方式:在建構子呼叫上層類別的建構子

為了解決上述的問題,就必須在建構子呼叫上層類別的建構子,確保子類別每次實體話的時候也能夠初始化屬性。

在上述例子中,只要修改Father類別,讓他在建構子當中初始化就可以了。讓我們用JavaScript中經典的prototype base繼承法來取得並執行上層物件的建構子,修改之後的Father類別如下:

//上層類別
function Father() {
    this.base = Grandfather;
    this.base();
    this.setBirthdayYear(1915);    
}

加入了中間那兩行便可以呼叫上層類別的建構子,透過這種指定方式,this.base會被當做是Grandfather這個function(也就是Grandfather的建構子),然後在下面呼叫this.base()的時候,就會把Grandfather做過的事情再做一次,也就可以達到呼叫上層建構子的這個目的了。

除了這個方法之外,也可以使用call()函式來取得上層建構子。以call()來修改的Father類別如下:

//上層類別
function Father() {
    Grandfather.call(this);
    this.setBirthdayYear(1915);    
}

call()會將Grandfather裡面的this以輸入的參數取代並執行一遍,而現在輸入的參數是Father的this,因此就等於Father中去執行Grandfather的建構子一樣的意思。call()方法可以說是比base更為簡潔且直覺的實作方法,詳細的內容可以參考ECMA-262的13.2.1 [[Call]]方法的定義

最後讓我們重新看一下以call()修改之後的四個類別多層繼承的程式碼:

//最上層類別
function Grandfather() {
    this.birthday = {
        year: null,
        month: null,
        day: null
    };
    this.setBirthdayYear(1889);
}

//屬性
Grandfather.prototype.birthday = {
    year: null,
    month: null,
    day: null
};

//方法
Grandfather.prototype.setBirthdayYear = function (year)
{
    this.birthday.year = year;
}; 

//上層類別
function Father() {
    Grandfather.call(this);
    this.setBirthdayYear(1915);    
}

//Father繼承Grandfather
Father.prototype = new Grandfather();    

//子類別之一
function Son() {
    Father.call(this);
    this.setBirthdayYear(1943);    
}

//Son繼承Father
Son.prototype = new Father();    

//子類別之二
function Daughter() {
    Father.call(this);
    this.setBirthdayYear(1945);    
}

//Daughter繼承Father
Daughter.prototype = new Father();

//輸出檢驗
var grandfather = new Grandfather();
var father = new Father();
var son = new Son();
var daughter = new Daughter();

document.write(grandfather.birthday.year);    //顯示1889
document.write(father.birthday.year);    //顯示1915
document.write(son.birthday.year);    //顯示1943
document.write(daughter.birthday.year);    //顯示1945

結語

由於JavaScript是傳址的方式運作,所以在物件導向上會發生很多出乎意料之外的問題,讓我在這兩個禮拜一直在找解決的方法。這一篇寫了又改、改了又寫,反反覆覆地好多次,總算是把JavaScript用prototype的繼承方法有個比較完整的整理。網路上的資料零零散散地看了很多,大致上把比較有印象的列在下方供大家參考。寫這篇也是對自己這些日子學習JavaScript的繼承方法有一個交代,作為學習的一個筆記。

原本我用prototype的方式來寫JavaScript的繼承,但因為發現了這個問題,於是又想改用Dean Edwards的Base工具來實作繼承,但是這卻導致Aptana這個JavaScript IDE看不懂,讓IDE整個武功盡失。在找尋更為理想的解決方式時,又回來整理這一篇。

image

到目前為止,我發現Aptana IDE可以完全理解這種prototype繼承方法,甚至不需要用JSDoc的@extends標註繼承的對象,Aptana也可以理解並帶出上層類別的方法或屬性。

接下來我想要繼續研究一個可以支援我需要的物件導向功能,並且又能讓Aptana這種JavaScript IDE讀取分析的理想實作方式。待我程式實作到一定程度時,確認都沒有問題了,我再把我的方法整理之後寫上來吧。


參考資源

(more...)

JavaScript中參數的傳值與傳址心得

JavaScript中參數的傳值與傳址心得

image
JavaScript中參數傳遞到底是「傳值」(passing by value) 還是「傳址」(passing by refernce) ,似乎常常造成許多人混亂。我自己原本也一直以為JavaScript只是傳值而非傳址,而為此混亂了好一陣子。特別是在撰寫物件導向的JavaScript程式時更容易造成混亂。
有人認為JavaScript的函數(function)會依據你輸入參數的資料類型來區分傳值還是傳址,物件(object)的話會使用傳址,非物件的則是使用傳值。但實際上,我認為JavaScript的本質是傳址而非傳值。而端看你在函數裡面是否修改參數的記憶體位址(也就是建立新的資料給參數),來決定是否影響函數之外的參數資料。
以下我們就來一一地檢視一下JavaScript究竟是傳值或是傳址的參數傳遞運作方式吧。

2016/1/14更新:根據底下留言網友討論指出了我原文的錯誤,可以發現其實JavaScript本質仍是「傳值而非傳址」。雖然下面文章仍放著給大家參考,但下面的討論更有看頭。希望觀看這篇的讀者能夠連下面的討論一併閱讀,方能深入瞭解JavaScript的奧妙。

參數傳值:Number、String、Boolean等非Object類型

當輸入給函數的參數類型為非物件(object)的資料型態,例如Number(數字)、String(字串)或是Boolean(布林邏輯值)的時候,JavaScript的函數會以傳值的方式來處理。傳值的意思是說,輸入到函數裡面的參數會被複製一份,而在函數裡面對參數的操作,不會影響到外在參數的影響。
以下是一段示範用的程式碼:
<script type="text/javascript">
//宣告變數,資料類型為字串
var paramA = 'original';

//確認變數資料
document.write(paramA + '<br />');    //輸出 original

//定義函數,在函數中修改參數資料,並且輸出做確認
function changeParamA(paramA)
{
    //修改參數
    paramA = 'changed';
    
    //確認被修改之後的參數資料
    document.write(paramA + '<br />');    //輸出 changed
}

//在函數中修改參數
changeParamA(paramA);    //在函數中運作,輸出 changed

//被函數修改之後,仍然維持原本的資料
document.write(paramA + '<br />');    //輸出 original
</script>

上面的例子的輸出結果將會是:
original
changed
original

由此可知,JavaScript函數中使用參數為字串資料類型時,將會是以傳值的方式運作。同樣的道理也適用於數字、布林值上。

參數傳址:Object

當輸入給函數的參數資料類型為Object(物件)時,JavaScript會以傳址的方式來處理。傳址的意思是說,輸入的函數裡面的參數只是「記憶體中的位置」,而在函數裡面對參數的操作,其實是對記憶體位置中的該物件進行修改,因此函數之外的參數也會受到影響。
以下是一段示範用的程式碼:
<script type="text/javascript">
//宣告變數,資料類型為物件
var paramB = {
    attr: 'original'
};

//確認變數資料
document.write(paramB.attr + '<br />');    //輸出 original

//定義函數,在函數中修改參數資料,並且輸出做確認
function changeParamB(paramB)
{
    //修改參數。注意修改的方式,是直接指定物件的屬性進行修改,而非建立新的物件。    
paramB.attr = "changed";
    
    //確認被修改之後的參數資料
    document.write(paramB.attr + '<br />');    //輸出 changed
}

//在函數中修改參數
changeParamB(paramB);    //在函數中運作,輸出 changed

//被函數修改之後,物件的屬性也跟著改變了
document.write(paramB.attr + '<br />');    //輸出 changed
</script>

上面例子中輸出的結果將會是:
original
changed
changed

在上面的例子中,你可以發現到以物件資料型態輸入函數中的參數,在函數中被修改之後,在函數之外也會跟著受到影響,一般來說這會被視為傳址的參數傳遞方式。

再探Object的參數傳遞

在上一節介紹傳址的例子中,函數修改參數的方式是指定物件的屬性進行修改。但是如果函數修改參數的方式是直接指定新的物件、或是輸入其他的資料類型,那麼函數之外的參數就不會受到修改,也就是傳值的參數傳遞。
以下是一段示範用的程式碼:
<script type="text/javascript">
//宣告變數,資料類型為物件
var paramC = {
    attr: 'original'
};

//確認變數資料
document.write(paramC.attr + '<br />');    //輸出 original

//定義函數,在函數中修改參數資料,並且輸出做確認
function changeParamC(paramC)
{
    //修改參數。注意修改的方式,是建立新的物件,而新的物件裡面也包含attr屬性。
    paramC = {
        attr: 'changed'
    };
    
    //確認被修改之後的參數資料
    document.write(paramC.attr + '<br />');    //輸出 changed
}

//在函數中修改參數
changeParamC(paramC);    //在函數中運作,輸出 changed

//被函數修改之後,仍然維持原本的資料
document.write(paramC.attr + '<br />');    //輸出 original
</script>

上面例子中輸出的結果將會是:
original
changed
original

你會發現到,即使輸入參數的資料類型為物件,但是當你在函數中為參數指定新的資料的時候,函數之外的參數並不會受到影響,是為傳值參數傳遞的運作方式。

推測JavaScript是傳址方式運作

因此由以上的例子中,我可以得到一個推測的結論:JavaScript一直都是用傳址的參數傳遞。只是根據函數對於參數是否變更參數的記憶體位址,而會有看起來像是傳值或傳址的運作差異。
在以物件傳遞、修改物件屬性的paramB例子中,函數本身並沒有修改參數的記憶體位址,也就是沒有給予paramB新建立的資料(新的資料就是一個新的記憶體位址),因此在函數中被修改的paramB,函數之外也會受到影響。
而paramA跟paramC的例子裡都是建立了新的資料給參數,修改了參數本身的記憶體位址,讓函數裡面運作的參數跟函數之外兩者互不相干,因此乍看之下就類似傳值的運作方式。

參數傳值或是傳址運作:Array

那麼我們再回頭來看看JavaScript中一種特殊的資料型態:Array(陣列)。陣列本質上屬於「Object」,那麼把陣列作為參數輸入函數時,究竟是傳值還是傳址呢?由前面的推論中可知,這是根據函數裡面對於參數的操作是否參數為建立新的資料來判斷
以下是一段示範用的程式碼:
<script type="text/javascript">
//宣告變數,資料類型為陣列
var paramAry = ['original'];

//確認變數資料
document.write(paramAry[0] + '<br />');    //輸出 original

//定義函數,在函數中修改參數資料,並且輸出做確認
function changeParamAry1(paramAry)
{
    //修改參數。注意修改的方式,是建立新的陣列。
    paramAry = ['changed'];
    
    //確認被修改之後的參數資料
    document.write(paramAry[0] + '<br />');    //輸出 changed
}

function changeParamAry2(paramAry)
{
    //修改參數。注意修改的方式,是修改陣列裡面的索引,而非建立新的陣列。
    paramAry[0] = 'changed';
    
    //確認被修改之後的參數資料
    document.write(paramAry[0] + '<br />');    //輸出 changed
}

//在函數中修改參數
changeParamAry1(paramAry);    //在函數中運作,輸出 changed

//被函數修改之後,仍然維持原本的資料
document.write(paramAry[0] + '<br />');    //輸出 original

//在函數中修改參數
changeParamAry2(paramAry);    //在函數中運作,輸出 changed

//被函數修改之後,參數的資料已經被修改
document.write(paramAry[0] + '<br />');    //輸出 changed
</script>

上面例子中輸出的結果將會是:
original
changed
original
changed
changed

因此你可以發現到,根據函數對於參數的操作,就會造成傳值或傳址的運作差異。

結語

當JavaScript越寫越複雜之後,對於參數的傳值或傳址就會越來越注重。也許是我讀的JavaScript的書不夠多,像這種基礎的概念居然都不是從書上得來,而是在網路上找尋各個程式設計師的心得與探討之後,再整理出來的。
題外話,因為這篇主要是整理理論的東西,沒什麼圖片。秉持著一篇文章一定要有一張圖的精神,就去ICON FINDER找了張JavaScript的圖示來貼在開頭XD 而最近都在談JavaScript的東西,因此我也為這個Blog加入了JavaScript的分類標籤。之前的發文就待有空時再來整理歸類到JavaScript標籤去吧。

參考資源

(more...)

Spket IDE使用jQuery自動提示

Spket IDE使用jQuery自動提示

image

Spket IDE是網路上知名的JavaScriptXML編輯器,有許採用jQueryExtJS工具庫的開發者都很喜歡使用Spket,而我在找尋理想的JavaScript IDE的時候,也來安裝Spket IDE來使用看看。

自動提示(Code Assist)是IDE中很重要的功能之一,效果就如上圖一樣,讓程式設計師在撰寫程式的時候,自動帶出相關的字詞,不僅減少程式設計師打字的工作量,也能夠避免輸入錯誤語法的人為失誤。然而似乎不少Spket IDE的使用者不清楚如何使用jQuery或ExtJS的自動提示功能,不僅Skpet IDE官方網站上有文字影片(簡體字?)的說明之外,Google中搜尋Spket IDE的介紹中也幾乎都是在教怎麼配置Profile

然而我照著教學上面的方法來做,卻始終帶不出jQuery的自動提示。找尋許多資料之後,終於在Spket IDE的論壇中發現了解決方法,因此想說在此整理一下,給有需要的朋友們一個引路。

環境敘述

image

Spket IDE可以作為plugin安裝在eclipse系列 3.2.x的IDE當中,而我是使用Aptana Studio 2,這是使用eclipse 3.5.2為基礎的IDE,也一樣可以安裝Spket IDE。而作業系統是Windows 7 64位元。

用網站安裝無效

image

原本我是使用eclipse的Install Software,連到Spket IDE的網站 http://www.spket.com/update 去下載1.6.18版本來安裝,但是這個版本似乎有問題,所以安裝之後仍然無法順利使用。

手動下載Plugin並安裝

根據論壇的說法,我將我的Skpet IDE安裝以及jQuery Profile配置的方法整理如下:

  1. 先安裝eclipse系列的IDE編輯器,例如Aptana Studio 2
  2. 下載com.spket.js_1.6.18.jar (SkyDrive備份)。
  3. 將com.spket.js_1.6.18.jar放置到eclipse安裝目錄底下的「plugins」資料夾。以Aptana Studio 2來說,就是「D:\Program Files\Aptana Studio\plugins\com.spket.js_1.6.18.jar」(因為我把Aptana安裝到D磁碟分割去了)
  4. 開啟eclipse,沒有錯誤訊息的話,應該就是安裝完成了。

接著是配置jQuery Profile:

  1. 打開「Window > Preferences」:
    image
  2. 左邊導覽列中,找到「Spket > JavaScript Profiles」,然後點選右上角的「New」以建立新的Profile:
    image
  3. 輸入Profile名稱,你看得懂就好,那就設定為「jQuery」吧。
    image
  4. 現在Profiles裡面多了一個剛剛建立的jQuery Profile,選擇他並點選右邊的「Add Library」:
    image
  5. 在選擇Library的下拉式選單中,選擇「jQuery」,然後按下ok完成:
    image
  6. 接著要加入jQuery的檔案,請點下右方的「Add File」:
    image
  7. jQuery的檔案最好是附加上說明的詳細版本,而不是一般使用的min版本,這樣在自動提示的時候才會把方法的說明也一併帶出來。在此推薦使用阿良翻譯的jQuery 1.3.2文件 (SkyDrive備份),請解壓縮之後選擇裡面的「jquery-1.3.2-jsdoc-Spket-profile.js」即可。
    image
  8. 完成後,選擇左方jQuery的Profile,並按下右邊的「Default」,讓Spket IDE把這個Profile作為預設的環境。
    image

儘管他們的版本號都是1.6.18,可是論壇上面的jar似乎比較正確。安裝正確的版本之後,不僅可以顯示jQuery的自動完成提示,也可以開啟Profile Explorer來查詢可用JavaScript語法。如下圖:

image


結語

儘管總算讓Spket IDE可以讀取jQuery的自動提示了,但是對於自己寫的程式,Spket IDE依然是不會弄出自動提示。接著我要繼續研究怎麼讓我寫的程式加入Spket IDE的自動提示中,如果這點可以做到的話,那麼Spket IDE就幾乎稱得上是理想的JavaScript IDE了。

(more...)

eclipse開啟時發生無法載入「jvm.dll」問題的解決方法

布丁布丁吃布丁

eclipse開啟時發生無法載入「jvm.dll」問題的解決方法

image

eclipse是知名的自由(也可以免費取用)的跨平台整合開發環境(Integreated Development Environment,簡稱IDE),主要用來開發Java,但他擁有強大的自訂能力,而使得eclipse也可以用來開發C/C++PHP或甚至是JavaScript。而許多IDE也是基於eclipse再擴充、發展而成,像是我目前主要用來開發JavaScript的Aptana Studio 2

問題敘述

eclipse能夠跨平台是基於Java運作環境的功勞,而他本身也是一個免安裝的檔案,理論上在良好設定的前提下,下載之後、解壓縮並直接開啟就能夠啟動。

最近為了再次找尋更完善的JavaScript IDE,所以我下載了Eclipse IDE for JavaScript Web Developers來使用。但沒想到下載完、解壓縮並開啟之後,出現了「Failed to load the JNI shared libray “D:\Program Files\Java\jre6\bin\client\jvm.dll”」的錯誤訊息,讓eclipse無法順利啟動。

這個問題顯然是我的Java環境參數哪裡弄錯了。我的電腦是Windows 7 64位元,安裝了Java的JDK 6.0.21,除了安裝路徑移至D磁碟分割之外,其他都跟預設一樣。

修改eclipse.ini,失敗

image

網路上可以找到兩種解決方法,一個是修改位於eclipse目錄底下的「eclipse.ini」設定檔。這個作法在「eclipse打开出现JVM terminated.Exit Code=-1错误的解决办法」這篇的後半部有提到,但我試著做卻很遺憾地沒能解決這個問題,而是需要用另一種方法來解決。

開啟eclipse時指定Java機器,成功

image

另一個方法是在開啟eclipse的時候,同時指定正確的Java虛擬機器路徑作為參數。作法如下:

  1. 找到你Java虛擬機器的路徑。以我的電腦為例子,因為Java安裝到D:\Program Files\裡面去了,所以路徑為「D:\Program Files\Java\jdk1.6.0_21\bin\javaw.exe
  2. 為「eclipse.exe」建立捷徑「eclipse.exe - 捷徑」,名稱可以隨意修改。
  3. 在捷徑上按右鍵,進入「內容」。
  4. 在「捷徑」分頁中找到「目標」欄位。
    image
  5. 在目標欄位資料後面加上「-vm "D:\Program Files\Java\jdk1.6.0_21\bin\javaw.exe"」,參數即是第一步中查詢的Java虛擬機器的路徑。舉例來說,原本的資料為「"D:\Program Files\eclipse\eclipse.exe"」,現在改為「"D:\Program Files\eclipse\eclipse.exe" -vm "D:\Program Files\Java\jdk1.6.0_21\bin\javaw.exe"」。
  6. 點擊捷徑,開啟eclipse。

image

開始使用eclipse吧!


結語

這其實是我第三次還是第四次遇到同樣的問題,而我卻每次都還是上網去找解決方法,找得灰頭土臉的才得到每次都一樣的解決方法。所以在此特地把這個方法寫在Blog,希望自己下次不要再這麼辛苦了。

題外話,Eclipse IDE for JavaScript Web Developers的JavaScript Editor在Aptana當中已有內建,而且也沒有Aptana JS Editor好用,著實讓人失望。下次有機會我在來分析一下JavaScript IDE的優缺點吧。

(more...)

論文進度報告(2010/9/26):進度延後至10/29

布丁布丁吃布丁

論文進度報告(2010/9/26):進度延後至10/29

image

9月中meeting時,我跟老師報告了一下系統狀況與現在的進度。當然就如你所知的,系統開發並不是很順利。前端的JavaScript程式完成了估計數量的1/3不到而已,之前說想要在9/21完成的希望,當然也是只能摸摸鼻子地再來重新估算日期。

系統現況與進度報告投影片

這是9/15論文進度報告的投影片(SkyDrive下載)。

前端的View部分,系統目前可以分成六大區塊:Toolkit工具箱、Core核心元件、Window視窗元件、Toolbar工具列、Text標註、Search搜尋。而目前我正在做到Toolbar這一塊,比報告之後又多推了一些進度,但跟上次報告時比起來也不過是多作了7隻程式而已,中間相隔約2個禮拜。

以下是進度的詳細內容:

元件 預估程式數量 已完成 UML類別圖
Toolkit 15 15 1
Core 15 13 1
Window 4 4 1
Toolbar 36 3 2
Text 38+? 0 3
Search 6+? 0 1
統計 114+? 35 (30.7%) 9

專案進度延後,設定10/29作為查核點

image

系統延後的狀況實在是太嚴重,嚴重到我都快對不起KALS Wiki左上角的倒數計時器。但是正視自己的弱點才能夠前進,所以還是要忍辱地再次調整專案規劃。

如果以兩個禮拜可以增加7隻程式來計算,大概還需要5個月的時間……當然不可能會到這麼久。而且這兩個禮拜中,又是寫書校稿、又是假期回家,所以進度才比慢到這麼誇張。不過都已經9月底的現在,說什麼理由也沒啥意義了。

總之,接下來兩個禮拜應該可以專心在系統上,因此要用這兩個禮拜來預估系統進度,然後設定10/29(五)作為查核點,再給自己一個月,看看情況如何吧。

image

由於整個進度都往後順延,影響到最後畢業的時程,延遲至3月初。由於我仍欠國中圖一篇論文,所以千萬得在3/24之前完成口試才行。

寫書一校完畢

image

之前提到的寫書工作,終於完成一校的作業了。

一校作業是由我跟老師分擔負責,我主要是檢查技術部分是否有問題。而不出所料地,我又作了很多修改。讓人有種「校稿怎麼校都校不完」的感覺。後來編輯又來告知說我圖檔dpi不足300,印刷時會讓圖片花掉,讓我又花了好多時間再來重新截圖。儘管現在都做完了,但二校時可能也會再來一次這種大混亂吧。

雖然老師希望該書在9月底能夠出版,但看現在的情況,能在10月底之前出版也許就該偷笑了?

與外國作者討論程式的感想

image

我在撰寫論文程式時,使用了Max Wheeler的PlaceHeld的外掛,並改進了裡面的一些小bug。這次心血來潮,上github將我的建議寫給作者Max,而他也在數天之後回了我。但他並不知道這樣改的意義,所以我又寫了一些範例回給他,現在在等他的回應。

以往我改了很多人寫的程式(特別是DSpace),有不少會在這個Blogger中寫出我改過的地方,但卻很少跟原作者交流、互動。我想,如果要成長的話,就一定要積極地與人交流知識才行。光是閉門造車,是很難有所成就的。

嗯,加油!


以往我總是將許多主題混在論文進度報告裡面一起寫,讓閒聊的事情與可能比較有教學價值的文章內容混淆。現在我打算把一些具有獨立探討價值的議題從論文進度報告中拆開來,進度報告歸進度報告(還有很多閒聊)、其他議題歸其他議題。也許這樣會對想要一次看完的讀者來說比較辛苦,但我相信將議題獨立探討,應該是更方便讀者找尋資料。

也因此,最近我發了很多篇文章,而談論議題都比較獨立。事實上,也還有好幾篇想寫議題列在待辦事項中,等待我一篇一篇地將他們完成。

(more...)

「布丁布丁吃?」Blog變更記事

布丁布丁吃布丁

「布丁布丁吃?」Blog變更記事

image

最近又調整了一下「布丁布丁吃?」這個Blog,不知道大家有沒有發現到一些細微的變更?

加入評分與反應功能

image

上圖中,右邊的五星是「評分」。左邊的五星是現在評分者的人數以及平均的星等,右邊是允許讓使用者進行評分的功能。歡迎大家使用此功能來對我的文章評分,有些寫得很爛、很隨便的,也請不要客氣地給予一顆星的低分吧!(雖然不評分的可能性可能更高?)

下方一排則是「反應」,就是四個選項的投票,可以由Blog管理者,也就是我來制訂選項的內容。目前我只有想到「加油」、「好玩」、「有趣」、「實用」這四個選項,不知道瀏覽「布丁布丁吃?」的您是否還有其他反應呢?請務必告訴我,我會把您的反應也加入選項當中的!

其實這兩個功能出現在Blogger設定區中已經好一段時間了,我之前也有預設把他加進去,但是最後顯示時卻沒有成功地叫出這個功能。我推測可能是我的樣板仍是使用舊版Blogger的樣式,後來去調整了一下樣板的內容,最後才帶出了「評分」與「反應」這兩個功能。

到目前為止,我還沒看過除了我自己之外有其他人會對我的文章評分。而即使評分了,系統也不會用e-mail告訴管理者,因此很難讓人發覺有人在使用這些功能。即使如此,多一些地方與讀者互動,總是比較有趣的。套句老陳的說法:「這也許可以成為一個研究的議題」XD

訪客留言板

image

自從我上次寫了「布丁式Blogger訪客留言板」之後,發現到「布丁布丁吃?」右邊多了一個「布丁布丁來聊天」訪客留言板的人就多了起來。這是從CBox改為Blogger內建文章留言功能的訪客留言板,技術與安裝上請看訪客留言板的介紹,此處不贅述。

總之,請大家多多來跟我聊聊天、互動互動吧。

留言移除驗證碼,使用垃圾留言管理功能

近來有朋友抱怨那驗證碼功能有問題,而我也發現到Blogger推出了垃圾留言管理功能,所以我也就跟電腦玩物一樣,關閉驗證碼而使用垃圾留言來管理大家的留言內容。

image

自從我關掉驗證碼功能之後,Blog中的第一篇內容就一直有不明人士留言。除了一開始的一兩篇需要我去手動將之設定為垃圾留言之外,之後的幾篇Blogger都很聰明地將垃圾留言抓了出來。

image

被抓出來的垃圾留言並不會顯示在前台的文章中,也不會像以前一樣顯示「留言已被刪除」的訊息,而是有如一開始就不存在似的被人忽略。但是我的信箱還是會一直收到有垃圾留言的通知就是了。

如果你的留言被Blogger當做是垃圾留言的話,請使用訪客留言板來告知我,我會儘快把留言復原的。

其實身為Blogger管理者的我,留言是不需要輸入驗證碼的,所以要不是我朋友不說,我自己還沒發現到這點XD

加入自動大綱的功能

image

現在在「布丁布丁吃?」閱讀單篇文章的時候,文章開頭會列出該篇文章中標題作為簡易的大綱目錄。讀者可以看看此大綱,了解一下這萹文章在講哪些主題,再考慮是否要繼續閱讀下去。

其實這是對自己找資料比較方便,我還蠻常回頭看「布丁布丁吃?」來找資料的XD

飛速成長的Blogger有感

image

在調整Blogger的時候,發現到Blogger加了好多之前沒見過的小工具。像是可以把文章分享到其他社交平台的「Share it」、列出最新文章的「Recent Posts」以及列出最近回應的「Recent Comments」。這些都是以往大家很辛苦地去hack Blogger版面才能得到的功能(就是右邊導覽列的那些啦)。

image image

使用這些小工具的話,網頁載入速度會更快,而不用像我這樣要等待AJAX讀取才能顯示資料。儘管Recent Comments(上圖中的左)跟Recent Posts(上圖中的右)預設的樣板可能不太好看,但經過CSS調整之後,應該也是可以呈現出美觀的版面。最主要的是,安裝跟設定變得非常簡單,應該會受到不熟程式的使用者歡迎。

但是這些功能的自訂性可能就沒有這麼高了,在以往我寫的Blogger專用最新文章與最新回應產生器中,有讀者提到是否能夠在最新回應中加入文章的標題。應該可以做到,但是要研究一下就是。我已經把這個議題記著,之後有空的話再來修改最新回應的功能。

 

image

此外,Blogger的「繼續閱讀」功能也更趨完善,插入一個註解標籤就可以讓Blogger去判斷繼續閱讀的位置,跟以往要用Tag將文章包起來的手法差很多。雖然像我這種連安插一個標籤都很懶得人來說,繼續用布丁式Blogger自動摘要功能還是很不錯用的。


結語

基於上次「訪客留言板」的經驗,我發現功能要寫出來,讀者才會發現他的存在。所以這一篇除了是日記文之外,也順便為這些功能打些廣告,歡迎大家多多使用。

對於「布丁布丁吃?」的改變,不知道你有什麼看法呢?對於「反應」的四個選項,你是否有什麼建議呢?可以的話,請不要客氣地跟我說說吧,感謝!

(more...)

將docx檔案解壓縮,抽取文件內的圖片

布丁布丁吃布丁

將docx檔案解壓縮,抽取文件內的圖片

image

Microsoft Office系列WordPower Point一向是文書處理工具中最常見的工具,我也使用Word 2003以前的doc檔案格式與老師討論論文、處理寫書的內文。但是當我在修改Word檔案時,發現到檔案中有部分圖片要修改的時候,這可就頭大了。因為Word內建的繪圖功能不像繪圖軟體一樣地強大,在Word當中也不好編修,所以我需要將圖片從Word裡面抽取出來

說來這方法也不難,因為Microsoft Office 2007之後改用了Microsoft Office XML格式,裡面的檔案本來就是可以解壓縮成XML檔案,只是我在Plurk聊到這件事情的時候,發現好像很多人不知道,所以心血來潮就用這篇來把相關的知識都整理一下吧。


什麼是Microsoft Office XML格式

Microsoft Office早期採用了封閉的格式,像是Word的doc、Power Point的ppt、Excel的xls等,主要用意是為了保護自己軟體的市場,強迫使用者必須購買Office才能開啟這些檔案。

然而封閉的態度抵擋不過時代的潮流,在競爭對手IBM、Sun公司推動開放文件格式(OpenDocument)的努力之下,美國政府宣布政府文件必須採用OpenDocument格式,而將封閉的舊版Microsoft Office以及其他專利軟體宣告出局。

為了提昇對於政府部門的長期信心,Microsoft Office 2007的推出改用了新格式:Office XML Formats (縮寫:Open XML、OpenXML、OOXML)。Office XML Formats有幾個特點:

  • 壓縮檔案:最高可將檔案壓縮至原始大小的25%。
  • 改良的受損檔案復原:即使圖表受損,檔案仍可以開啟。
  • 輕鬆偵測包含巨集的文件:以字尾「x」儲存的檔案(例如.docx、.pptx)不能包含巨集,以字尾「m」儲存的檔案才可以。
  • 對個人資訊提供更理想的隱私權保護與更嚴密的控管
  • 更完善地整合並互通商業資料:只需要ZIP公用程式和XML邊集氣就可以開啟和編輯Office檔案。

微軟積極地推動Office XML Formats,終於在2008年讓他成為ISO國際標準。但是這個標準受人爭議,有不少開放文件格式(OpenDocument)的支持者對反對Office XML Formats成為國際標準,新聞媒體也將Office XML Formats跟OpenDocument之間的競爭稱為「文件格式大戰」。

時至今日,Office 2010也已經順利推出,採用Office 2007以上版本的使用者也越來越多。在Office 2003以前的使用者可以安裝檔案格式相容性套件來讀取Office 2007以上的檔案格式,而OpenOffice.org 3也提供匯入docx、pptx以及xlsx等Office 2007以上檔案的相容性。整體來看,Microsoft Office仍會在你我身邊活上好一陣子吧。

解壓縮docx

背景聊完之後,接著我們來看看要怎麼解壓縮docx檔案,並取出裡面的圖片資料。方法很簡單,請按照以下步驟一步一步來做即可:

  1. 找到要解壓縮的docx檔案。如果您在電腦裡面看不到副檔名的話,請查詢「讓電腦裡的檔案顯示副檔名」的方法。
    image
  2. 將副檔名從「docx」修改成「zip」。系統會提示你「如果您變更副檔名,檔案可能會無法使用。您確定要變更嗎?」,請按下「是」來完成副檔名變更的動作。
    image
  3. 你現在可以看到他變成一個zip檔案,然後請用你熟悉的方式為這個zip檔案解壓縮即可。下圖的例子中我是使用中國大陸的「好壓」軟體,介面很像WinRAR,但是免費又支援7z壓縮,不錯用。
    image
  4. 解壓縮之後,你可以發現原本的docx檔案變成了一層一層的目錄,裡面包含著各種檔案。
    image
  5. 請從裡面翻找「word/media」資料夾,圖片檔案就在裡面了。
    image

利用Pismo直接「開啟」docx

image

我以前介紹過Pismo File Mount Audit Package這個將ZIP檔案直接當做唯讀資料夾開啟的工具。現在我們一樣可以用他來直接「開啟」docx的內部。

  1. 安裝Pismo File Mount Audit Package (下載位置),詳細操作請看我的介紹
  2. 在docx檔案上按右鍵,選擇「Quick mount」。
    image
  3. 如此一來,就能直接「開啟」docx檔案的內部資料。連改檔名、解壓縮等動作都免了呢。
    image
  4. 要還原成原本的docx的話,只要在被掛載的資料夾上按右鍵,選擇「Unmount」即可。
    image

從doc到docx

image

如果你的檔案原本是doc檔案的話,只要想辦法轉換成docx,就能進行像上述的動作。

如果你使用的是Office 2007以上版本,只要在儲存檔案時,將他另存新檔為*.docx格式即可。

如果你使用的是Office 2003以前的版本,那麼需要安裝相容性套件,才能儲存成docx。

pptx也一樣可以解壓縮

image

由以上介紹可知,Power Point的pptx也一樣可以用上述步驟來進行解壓縮、轉換。讓你可以用此方法輕鬆地抽取投影片裡面的檔案喔!


參考資源

(more...)