March 2009 - 文章

IE8-好用的CSS"試算"功能

慢慢開始發現IE8的Developer Tools有些過人之處,面對Firebug,不再只是被壓著打。

以下這個功能就深得我心!!

追出元素上套用的所有的CSS設定及其來源一點也不稀奇,是各大Browser輔助工具必備的本事,但IE8 Developer Tools的Style追蹤畫面上,每個設定前方都附了一個Checkbox,勾一下就可以立刻看出有設與沒設的差別,這個貼心設計用來找版面配置問題上犀利無比,讓我不禁想起立鼓掌。

另外,小磁片按鈕也很好用,看到喜歡的css, js, html,按一下就可以馬上存檔,雖然只是省去了Select All + Copy + Open Notepad + Paste + Save的小動作,但仍十分值得讚賞。

終於,我又從Firebug慢慢向IE傾斜了。(噗浪時例外,IE還是被排擠得一塌糊塗)

Posted 29 March 2009 04:28 AMJeffrey | 3 comment(s)
Filed under: ,
我的IIS6 Silverlight部署經驗

試著將Silverlight與WCF部署到IIS6上,發現眉眉角角的東西還真多。

首先IIS會抱怨找不到xap檔案,這是因為IIS6的MIME Type中沒有宣告這些附檔名的緣故: (IIS6對未宣告的不認識檔案類型,一律會假裝沒看到,這是安全上的考量)

* .xap     application/x-silverlight-app
* .xaml    application/xaml+xml
* .xbap    application/x-ms-xbap

Silverlight UI出現了,但接著是.svc的檔案傳回找不到的訊息,也是需要註冊手續:servicemodelreg -i,幸好以前經歷過,很快就處理掉。

接著svc傳來以下錯誤:

Security settings for this service require 'Anonymous' Authentication but it is not enabled for the IIS application that hosts this service.

依照網路上找到的資料,在IIS6中將svc檔案設為允許匿名存取,但仍無效。想到會不會是檔案權限影響? 果然,該目錄只放到Users讀取的程式,將檔案的NTFS權限開放NETWORK SERVICE帳號讀取,總算,Silverlight在IIS6的網站上活了起來。

【茶包射手專欄】ASP.NET在IIS7上讀不到Oracle中文

測試Silverlight的過程中,發現我的Web Application Project,使用Visual Studio 2008直接執行時測試正常,但移到IIS7下執行,由Oracle讀取的中文卻會變成問號。

我將程式簡化如下:

using (OracleConnection cn = new OracleConnection(cnStr))
{
    cn.Open();
    OracleCommand cmd = 
        new OracleCommand("SELECT '良好' FROM DUAL", cn);
    OracleDataReader dr = cmd.ExecuteReader();
    DataTable dt = new DataTable();
    dt.Load(dr);
    string ss = dt.Rows[0][0].ToString();
    byte[] b = Encoding.Unicode.GetBytes(ss);
    Response.Write(ss + " " + BitConverter.ToString(b));
}
Response.End();

預期要出現的結果應是,良好 6F-82-7D-59,在IIS7上則看到 ?? 3F-00-3F-00

一直在揣摩二者間的差異(ASP.NET Dev Server vs IIS7),想到該不會是x64做怪? 將Web App Project Build Target由Any CPU改為x86,變成以下錯誤:

Could not load file or assembly 'WCS.Web' or one of its dependencies. An attempt was made to load a program with an incorrect format.

研判與Application Pool同時有x64/x86並存有關,另外建了一個App Pool給WCS.Web,則Build成x86也可執行了,但仍無法解決亂碼的問題。

最後Google到一個可疑之處,IIS7的App Pool可以設定是否啟用32bit Application的選項。而之前在Windows 2008 x64裝x86與x64兩個版本的ORACLE Client的經驗刻骨銘心,讓我不禁懷疑與此有關...

預設為False,開啟為True後,可愛的中文終於出現了!!

Silverlight XAML在Blend2下會出錯

這陣子在趕一個小案子,目標是在網頁中實做一個可以凍結標題欄/列,可以任意調整欄寬、順序,並可以排序的大型資料表。

大家應該記憶猶新,不久前我才發表過GridView的標題欄、列凍結效果(跨瀏覽器版),其實為的也是同一個案子。不過評估之後,網頁版如果要加上用滑鼠拖拉調整欄位寬度、順序及動態排序的功能,若自己動手改,寫到牙歪了還不見得流暢無誤;找現成的商用WebControl元件也是解法,但考量這個資料表被用來監控大量數據的持續變化(跟股市看盤畫面有點類似),試了一下,光用HTML Table列出這麼龐大的矩陣就讓瀏覽器汗流浹背了,更甭提捲動時畫面慢如老牛拖車,我認為改用商用元件也不會太樂觀。

最後我做了個勇敢的決定 -- 就讓Silverlight試試身手吧! (現在想想,第一個Silverlight專案不是做紅綠燈或影片播放器,而是直接硬幹應用程式操作UI,真是不知死活藝高人膽大。)

Silverlight 2.0終於有了Datagrid Control,是讓我敢大膽嘗試的主要原因,內建凍結標題功能、可自由調整欄位寬度、順序,只要Bind上資料物件,物件屬性一改,顯示的數字就會自動改變(但別忘了實作INotifyPropertyChanged!),架構完整單純,比起在HTML中硬幹,省事許多。

不過,用真實的專案"邊做邊做"Silverlight 2.0,"非常"具有挑戰性,最棘手的部分莫過於Silverlight Datagrid的應用普遍性偏低,不管是坊間書籍或網路案例都著墨都不多,加上對Silverlight 2.0的基本認識如一片白紙,一路創下許多可觀記錄:

  • 依條件改變DataRow底色 --> 耗時4小時
  • 在DataRow間加入間隔線 --> 耗時3小時
  • 依正負值切換不同DataCell底色 --> 耗時1小時
  • 在ListBox上加上DoubleClick事件 --> 耗時10分鐘

這些寫網頁時原本要"秒殺"的任務,居然都變成以小時計,還深怕自己想出的解法是在繞遠路,極不習慣。感覺自己像是忽然中風癱瘓,連伸手拿杯水喝都要抖著手掙扎半天,搞到滿身大汗才能如願。但值得欣慰的是,解決問題所花的時間愈來愈短,算是漸入佳境。

流血流汗數日,雛型大致完成。一方面丟給User試用,另一方面也回頭做Refactoring。結果遇到有趣的問題,在Visual Studio 2008改完程式要去Blend2編輯UserControl時,出現以下錯誤。

同一個XAML在VS2008中可以正確預覽,實際執行也無誤,就只有在Blend2無法產生Design View(XAML View正常)。原本以為是XAML裡某個語法Blend2不吃,後來看了錯誤訊息中的StackTrace恍然大悟! 原來是程式會將使用者上回調整的欄位資料寫入IsolatedStorage,下回啟動時自動讀入還原。結果這段程式在Blend2中居然會被執行(在UserControl_Loaded中),Blend2中的環境當然不會跟網頁完全相同,因此出錯。

解決方法很簡單,之前在.NET 2.0裡有過判別DesignMode避開特定邏輯的經驗,這回如法泡製即可。我找到一篇文章,介紹用DesignerProperties.GetIsInDesignMode(Application.Current.RootVisual)就可以做出如.NET 2.0 DesignMode屬性的效果。

避開只有在Web中才適用的邏輯,問題解決!

IE8!!!

配合MIX09在拉斯維加斯正如火如荼,IE8正式版也在台北時間3/20凌晨3點開放下載(看了一下,IE8 Beta1則好也是配合MIX08開放下載的,看來辦活動配合釋出新版炒熱話題已成為一種模式)。一天到晚與Browser為伍的我,二話不說,三兩下就把它給Download回來,四處看看有沒有什麼新發現,五分鐘後有了以下初步心得...

首先我注意到,輸入URL時,提示過去連過的URL時會附上文件Title,不過有個小缺點是,URL的後半截看不到了,有時一堆URL的Title看起來完全一樣,根本分不出來。幸好把滑鼠移到提示URL上,會以Tooltip方式顯現URL全文。

多了Title的好處是可以用關鍵字從Title查詢到訪過的網站。

另外發現了"建議的網站"功能,讓我想起.COM時代喊得震天價響的Cross Selling...

老在寫Script,最關心的莫過於Script Debugging了。IE8 Script的錯誤提示對話框變成以下這樣,然後Script Debugger內建囉! (用力鼓掌)

Develooper Tools原則上就是先前IE Developer Toolbar外掛的演進,但功能跟介面強化很多。

新加入的Profiler功能,可以看看你的Script效能,依Function區分統計能協助找出效能不佳的瓶頸。不過不像Firebug內建監控網路傳輸功能,頗有遺珠之憾。(只能靠FiddlerHttpWatch解決)

IE8有沒有比較快? 我沒有勤奮到搞出同樣的環境分別用IE7及IE8做測試,但試跑了最近幾天常在看的複雜網頁(內容很多,Script跑很大),確實有感受到速度變快,而且差異還挺明顯的。不過因為沒有數字佐證,大家聽聽就算了。

各位Web Developer,從今天起就會開始有User名正言順用IE8來挑戰你的網站,別再逃避,早早將網站升級到"Ready for IE8"吧!

暫時想當鴕鳥無暇面對的人可以參考以下苟安之道:

Posted 20 March 2009 09:48 AMJeffrey | 10 comment(s)
Filed under:
jQuery教學6-8集 in MSDN

我的邊做邊學jQuery又有三集新內容上線。其實上週就上線了,但最近忙昏頭,拖到現在才貼文宣傳。順便廣告一下,不想錯過MSDN網站最新線上教學或市場新書資訊的人,可以訂閱MSDN Taiwan的Twitter RSS。偶爾還會有天上掉下來的贈書活動,想要撿好康的人可以留意一下。(不知將來會不會送XBOX 360當獎品?)

這次的三集內容主要著重於DOM元素的新增修改搬移刪除,以及事件處理,第8集則在HTML+CSS+JS不到100行的前題下,做了一個可以玩(但不怎麼好玩,呵)的拼圖遊戲,有興趣的人可以參考看看。

狗都嫌的Vista UAC,問題在哪裡?

前幾天接獲一件案例,Vista x64連上健保局網站,由於需使用自然人憑證,要安裝ActiveX控制項,結果苦主將IE7的保護模式關閉、也設成信任的網站,反覆嘗試,耗了超過一個小時,仍不得其門而入,接獲報案後,以過來人的經驗建議改用Run As Administrator執行IE,問題迎刃而解。

後續的說明中"不小心"提到Run As Administrator是解決大部分UAC障礙的大絕招(前題你要自行承擔該程式夾帶病毒、木馬等惡意程式的風險)。果不其然,在這個案例中又聽到再熟悉不過的評價: Vista的UAC真爛,難怪大家都把它關掉。

想一想,我跟一般User的思維不同,我愛死UAC了。有過跟木馬、駭客交手的經驗,我在資安議題上呈現"十年怕井繩"的輕微被害妄想。在使用Vista之前,我甚至裝過Comando Firewall,過濾所有聯外通訊,動不動就會跳出來問你: "某某程式現在要連到某某網站囉! 你要不要大發慈悲放行一下?",一天要按個數十下確定我都不為所苦。因為我知道稍不留神,木馬、病毒程式可能透過各式管道騙過防毒軟體鑽到系統中,每日開機關機長相左右,還可能正偷偷把我瀏覽的無名正妹在網頁上輸入的信用卡號傳送到惡魔黨總部。

木馬、病毒要能攻下系統,很重要的一個步驟是將自己注入成為系統的一部分,將自己設定為開機就執行或在系統核心加掛攔截程式(例如: 鍵盤側錄、封包監看),而這些動作有一個共同特色--都需要具備系統管理者權限才能執行。而大部分的使用者,以身為電腦的擁有者自居,自然不希望管理自己的電腦還處處受限,因此平時登入身份多具有管理者權限。這樣情境下就提供了許多可趁之處,使用者只要未及時更新系統漏洞,或用平時登入的身份跑了不該跑的程式,登登登,木馬病毒馬上就跟作業系統天人合一。

UAC等同於將以上的情境再做了切割,承認使用者具備管理者身份的事實,但在要進行任何更動系統核心的不尋常舉動前,額外跳一個視窗徵求使用者允許後才真的執行。

UAC的出發點是對的,但為什麼一天到晚被User打搶,嫌棄到爆? 為什麼對我來說,卻如獲至寶,愛不釋手?

在這次的案例裡我蒐集到一針見血的註解: UAC就是工程師思維下的產物,這麼技術性的東西該包在系統內部,根本就不應該丟出來叫User做決定。

一點也沒錯,基於技術上的瓶頸,OS沒有能力自行判斷哪些動作是有害的可疑行為、哪些是正常的操作(自己也寫程式,可以理解程式要冰雪聰明若此,何其困難!),因此才戒慎恐懼地將決定權丟回給User。

這聽起來有點不負責任,但就程式設計的思維並沒有錯,在Exception Handling的摡念裡,該段程式無法處理的狀況就該丟出去給上一層(呼叫端)的程式處理,遠比假裝沒事、粉飾太平來得正確。但RD忘了大部分的User比程式還無知,對於什麼情況需要更動系統亳無所悉,於是產生了三大類使用者: 依直覺按不同意然後功能跑不出來的User(很憤怒)、每次按同意然後被問到很煩User(也很憤怒)、聽人家說關掉UAC結果中毒,然後抱怨Vista比較安全是屁話的User(還是很憤怒)。

UAC真正能嘉惠的是像我這類對OS有基本認識,可以意識到程式目前在做什麼,有能力判斷該動作是否真的需要管理者權限去更動系統的Power User。

RD認為UAC像是手槍上的保險,保障的是生命安全。生命是無價的,所以再麻煩也是必要之惡。君不見發射核彈得同時插兩把鑰匙、掏出小抄唸密碼後才能成行,相較之下,開開保險栓只是舉手之勞,不解User在窮嚷嚷什麼。更激進一點,User不該只是傻傻地把電腦當微波爐用,為何不去進修多學些資安知識? 才不會一天到晚做些蠢事招來病毒木馬,把網路世界搞得烏煙瘴氣。

從User的角度出發,絕對無法苟同上述意見。除非身受其害,一般User很難想像Admin權限可以捅出多可怕的簍子,也無法體會RD想守護最後一道防線的苦心。在User的眼中,資安是軟體廠商要做好的本份,在Windows裡做任何事都應該要像拿杯子倒開水一樣自然,動不動就問一堆無厘頭的火星問題,強迫User做決定然後風險自負,荒謬透頂。

我悲觀地覺得,除非技術及規格上有革命性的突破(例如: 所有的程式元件能有一張好人卡讓OS明辨忠奸),這個僵局是無解的,充其量只是多犠性一些安全或多減損一些方便。過去我們將安全交給防毒軟體跟User自己把關,對於管理者身份執行的任何動作來者不拒,不會對使用者操作造成任何不便,卻給了病毒木馬許多可趁之機。Vista推出UAC,算是在"方便 vs 安全"的天平上向"安全"一端傾斜。但在這個當下,User心理上還無法接受"流汗總比流血好"的苦口婆心、知識上對於惡意軟體常用的入侵伎倆亳無所悉,結果是嫌UAC太吵卻又不能在關鍵時刻做出正確判斷,落得做到流汗、嫌到流涏的悲情下場。

技術上的限制及使用者意見的分歧讓UAC形同一份不便宜的意外險,值不值得加保端賴各人的風險意識。風險 = 損失 * 機率,如果你認為機器中毒被駭重灌就好,損失有限,則每天10元繳保費都嫌浪費;如果你覺得雖然防毒軟體失效的機率不高,但被駭的最糟結果可能惹禍上身甚至身敗名裂,則每天花100元買份保險都不算貴。該不該投保險看個人,沒有標準答案,UAC亦然。

jQuery.live()與Event Delegation

網友Ark剛好問到live(),不知道大家有沒有覺得jQuery.live()具有為"未來元素"設定事件的未卜先知能力很神奇? 至少在乍聽之餘,我覺得很不可思議,莫非它要攔截所有建立元素的過程? 不對,難度很高、沒效率、漏洞也很多,那麼又是怎麼做到的呢?

本草網目官方文件上有詳細的解釋,關鍵在Event Delegation

過去bind()時,會逐一在元素上設定事件,而Event Delegation則是利用事件會Bubble Up的特性,直接把事件掛在document上,網頁上的任何元素觸發事件時,document也會被觸發同樣的事件,透過event.target則可找出觸發事件的真實來源。live()就是透過傳入的Selector比對target元素是否為鎖定對象,若在Selector範圍內,就執行指定的事件函數。

這裡用個簡單的例子示範如何透過在document上掛click事件攔載<input type="button">的點擊。(有興趣的人可以在Mini jQuery Lab親手玩看看)

Body HTML為
<input type="button" id="btn1" value="Button1" />
<input type="button" id="btn2" value="Button2" />

Script為
$(document).click(function(e) {
    alert($(e.target).val() + " is clicked!");
});

如此,我們雖然沒有$("input:button").click(fn),一樣可以抓到按鈕的Click事件。

最後補充一下Event Delegation的優缺點。它的優點為:

  • 可降低Event Handler的數量,減少記憶體的使用,有利於效能與穩定性。例如那種要處理數萬個<td>事件的場合,就可以考慮。
  • 元素新增、移除、更動時,不用反覆掛載或卸除事件處理函數。

缺點則是:

  • 掛在document上的事件函數會在網頁上每一個元素被觸發事件時都被呼叫一次,由於執行頻率很高,若沒寫好可能重創效能。
  • 不是所有的事件都會浮到上層,例如: blur, focus, load, unload就不適用。
  • 應用在mouseover之類的滑鼠事件時需特別留意,由於呼叫頻率高,一樣要小心程式寫法以免拖垮效能。

了解了Event Delegation的持性,大家可以善用它有效率地滿足特定的情境需求囉!

半夜三點自動開機,電腦有鬼?

應該有些人有經驗,電腦在半夜三點自己開機,有不少人因此驚嚇不已,上網求救,Google一下,發現不少討論,還有些有趣的結論:

  • 一定是電力不穩啦,台電很爛
  • 你的電源供應器要換囉
  • 中毒或木馬,快去掃毒
  • 請平時多積德或至行天宮洽詢

昨夜我又遇到一次自動開機,加上凌晨發現有同事異常地早早登入MSN,詢問之下才知也是筆電無故自動開機的緣故。

發現好像很多人還不太習慣Windows的這種聞雞起舞的奮發,這裡分享一下心得。

半夜自動開機情形自己遇過好幾回,多發生在我將Vista切到Sleep狀況下。一開始的確也懷疑是電力不穩或網路(但BIOS Wake on LAN明明沒設定)、滑鼠、鍵盤的瞬間突波訊號(Sleep時,搖一下滑鼠就能啟動電腦),不過後來我發現有個巧合,檢查系統事件,開機時間一律是在半夜三點整,非常規律,應與系統設定相關,再參照了一下重開機後一定有某個相關的Log相伴---系統已更新通知! 沒錯,凶手就是自動更新。

搞清楚了這點,未來再遇半夜三點電腦自動開機(嚴格來說,應是醒來而不是開機),就不必再驚慌。(當然,若沒插電還發生就不在本文討論範圍囉) 避免方法是上床前看看有沒有待裝的更新(Vista的關機鈕上會有小驚嘆號),順手先安裝掉,就不會再有擾人清夢的狀況發生。

PS: 本想從官方網站上找到關於此一行為的陳述,但只在微軟這篇介紹Vista的Sleep模式KB裡稍微提到。順便一提,Vista的Sleep比傳統筆電在用的Standby功能更便利,會像Hibernation一樣將記憶體資料也寫入HD,萬一不幸斷電可從HD復原,不怕資料遺失。還有,自動更新後的重開,Windows設法會將原本開啟的視窗通通開回來,有時猛一看還搞不清楚有重開機過哩!

【2009-10-25更新】透過控制台電源相關設定,可停用自動開機功能

Posted 14 March 2009 09:25 AMJeffrey | 2 comment(s)
Filed under:
用SQL 2008 SSMS逐行偵錯

圖片說明一切!

是的,SQL 2008的SQL Server Management Studio可以直接Line-by-Line Debug!!! 不再需要由Visual Studio代勞。

剛好最近在寫很複雜的Stored Procedure,雖然未來上線的正式環境會是SQL 2005,我在開發平台上還是偷跑裝了SQL 2008,光這功能就值回票價,倒是T-SQL Inetllisense的功能沒有想像中靈光。(畢竟要在結構鬆散又彈性十足的T-SQL中要準確地找對提示複雜多了)

直接在SSMS中用Debug .NET程式的方式在Stored Procedure裡匐伏前進,讓人通體順暢! 設計貼心的IDE不愧是微軟的拿手絕活~~~

【茶包射手專欄】Word 2007網路檔案被鎖定

同事開啟一個網路資料夾的Word檔要編輯時,忽然發現彈出以下訊息。

這個訊息看來並不陌生,當網路資料夾裡的Office檔案被人編輯中,其他人再開啟檔案就會看到。但很絕的地方是--被指名是鎖定者的同事早早就請產假去了,總不會此刻在醫院待產室裡用Notebook連VPN回公司繼續奮鬥吧?

我們推斷是不正常斷線之類產生的程式錯亂,但怎麼解決呢? 查了微軟KB,發現Word的鎖定做法是在同目錄產生一個Owner File: ~$TableLayout.doc來標註鎖定者。很快地,我也果真在該目錄找到這個隱藏檔案,把它刪除問題就解了吧?

結果,砍了~$TableLayout.doc再開啟檔案,這次原先鎖定的同事果然乖乖去生小寶寶了,但檔案現在被名叫"另一個使用者"的人鎖定住了,搞什麼鬼!

我猜想,除了Owner File之外,doc檔案裡應該有某個地方標註了鎖定與否的狀態旗標。但我怎麼查就是沒人提到這種砍了Owner File還呈現鎖定的案例。最後是同事找到了解決方法,出奇簡單---存成本機副本再去覆寫網路資料夾裡的檔案就OK了,讓人氣短,哈!

PS: 覆寫法成功的前提是鎖定者的Word Process已中止,否則會因檔案在使用中而失敗。

SQL UDF-將CSV字串轉成SELECT結果

直接把CSV轉成可以直接SELECT內容的SQL函數? 我肖想好久了,今天終於動手把它寫出來了。

我原本參考一篇Google到的文章,其中的CTE完全用SELECT搞定的寫法很酷,我曾嘗試改為Table Variable放入UDF,但測試結果不理想,速度慢了一倍(五萬次 22秒 vs 10秒)。最後還是乖乖地用WHILE LOOP實作,程式如下:

ALTER FUNCTION SplitCSV
(    
    @csv NVARCHAR(4000),
    @delm CHAR(1)
)
RETURNS @Result TABLE 
(
    Element NVARCHAR(4000)
)
AS
BEGIN
    DECLARE @p INT, @lastP INT, @quit CHAR(1)
    SET @p = 1
    SET @lastP = 0
    SET @quit = 'N'
    
    WHILE @quit = 'N'
    BEGIN
        SET @p = CHARINDEX(@delm, @csv, @lastP + 1)
        IF (@p = 0) 
        BEGIN
            INSERT INTO @Result VALUES 
                (SUBSTRING(@csv, @lastP + 1, LEN(@csv) - @lastP))
            SET @quit = 'Y'
        END
        ELSE
            INSERT INTO @Result VALUES 
                (SUBSTRING(@csv, @lastP + 1, @p - @lastP - 1))
        SET @lastP = @p
    END
    RETURN
END
Javascript - 淺談this與Closure

網友kink問了一個問題:

$.fn.wait = function(time, type) {
       time = time || 1000;
       type = type || "fx";
       return this.queue(type, function() {
           var bb = this;
           setTimeout(function() {
               $(bb).dequeue();
           }, time);
       });
   };

在以上這段程式setTimeout呼叫的函數裡,為什麼不能直接用$(this).dequeue(),而要先var bb = this; 再$(bb).dequeue()呢?

在Javascript,this是一個奧妙的關鍵字(至少對我來說很玄,我在學習jQuery後才慢慢搞清楚它的用法)。我們先避談this在自訂物件時的應用(那又要講一大串)。簡單來說,this代表函數的擁有者(Owner),例如在事件裡:
$("input:button").click(function() {  alert($(this).attr("id")); });

this會指向觸發click事件的元素,原因是我們背地裡等同於將這個函數宣告成為元素的Method,元素成為函數的擁有者。setTimeout其實是window.setTimeout()函數,並不是某個物件觸發的事件,在這種情況下,this就會指向window,因此你可以試試:
setTimeout(function() { this.alert(this.document.body.innerHTML); }, 100);

又可以this.alert,又有this.document可用,可以證明this在當時指向的是window。

歸納一下,你可以想像成當函數被掛到某個物件上時,我們去呼叫它,this就會指向它所被掛載的對象(Owner),若前述這一點不成立,this則指向window。用個例子來說明:

function someFunc() { alert(this.document); }
var obj = new Object();
obj.doIt = someFunc;
obj.document = "Object's property";
someFunc();
obj.doIt();

執行這段程式,你會發現someFunc()會傳回[object](在FF中更明確,會是[object HTMLDocument]),而obj.doIt()則傳回"Object's property"。由此驗證,當函數變成了物件的一個Method時,this會指向該物件;直接呼叫函數本身,this指向window。

以上的實驗可以解釋不能在setTimeout中使用this的原因。既然不能直接用,初學Javascript的人(或像我一樣寫了七八年仍只用到皮毛的人)直覺想到的應該是宣告一個全域變數,然後在setTimeout中引用它。不過,用全域變數就要留意多次呼叫時變數名稱打架的問題。Javascript中,有一個更方便的解決方法--Closure。

Closure是程式語言上的一種概念(並不限Javascript),要徹底從學理上說清楚講明白並不是件容易的事,在此只對它的應用做點示範。(當然,真正的理由是我的能力範圍只到這個程度,XD)

var x = 1;
function say() { alert(x); }
x = 3;
say();

在這段程式裡,我們會得到3。但如果我們在宣告函數時x=1,就希望函數中的x不要受外界影響,怎麼辦?

function makeFunc(c) {
    var x = c;
    return function() { alert(x); }
}
x = 3;
var say1 = makeFunc(1);
say1();
var say2 = makeFunc(2);
say2();

在這個程式裡,我們宣告了makeFunc函數。函數裡設法產生一個Closure的環境,裡面宣告的var x接下參數c,跟當下建立的匿名函數綁在一起被傳回來。我們可以想像成,say1 = makeFunc(1)時,當場產生了一個匿名函數,並綁了值為1的x變數;say2 = makeFunc(2)時,現場產生了第二個匿名函數,並綁了值為2的x變數。這樣就滿足了每個函數需要擁有自已專屬變數的需求,會比用全域變數為每個函數保存一份乾淨俐落許多。

但可以想見這種寫法會耗用較多的記憶體,也有可能會導致瀏覽器發生記憶體洩漏(Memory Leaking)的狀況,IE6過去在這議題上惡名昭彰,但隨著不斷Update/Fix,差不多都更新改善過,我用過一些測試工具都已測不到Memory Leaking,應該不用太過擔心,在應用時有點警覺即可。

【延伸閱讀】

孤僻不群的IE7、兵臨城下的IE8

網友報了一個有趣的問題,他寫了一段Javascript Code,動態新增<input type="radio" name="r1">後再變更name屬性,試圖改變其群組性。但這段程式在IE7下不管用,在FF上卻運作正常,用IE Developer Toolbar檢視,發現name屬性的確被修改,但在IE裡所有的radio仍被算成同一群組,呈現互斥。

我把這個問題簡化,用Mini jQuery Lab做了測試,Body HTML為:

<div><input type='radio' name='a'> <input type='radio' name='a'></div>
<div><input type='radio' name='a'> <input type='radio' name='a'></div>

Script為:

$("div:last input").attr("name", "b");

測試各家瀏覽器,熟悉的情境再度上演。IE一國,其他的瀏覽器一國(FF, Chrome, Opera, Safari),不合群的IE7是唯一無法透過動態改變radio name調整群組性的瀏覽器。

最後搬出IE8來跑了一下,Good! IE8終於不再搞孤僻,乖乖地跟大家一樣,可以透過修改radio name來調整群組性了。

順道一提,IE8已經計劃在3/20推出繁體中文版,並從4/30起透過Windows Update方式強迫強力部署,由於IE8瀏覽器的行為與IE7存在不少差異,屆時應該會有一些原本IE7裡看得好好的網站在IE8裡面目全非,網站開發人員應留意。(讓人想起上回IE7全面更新)

如果發現網站還沒有準備好接受IE8的洗禮,目前有兩個方法可以避免立即的衝擊:

不過,IE8更貼近W3C的標準,在安全及效能上也有大幅改良,網站開發人員應該還是要設法讓網頁能在IE8裡正常顯示,才是長久之計。畢竟,躲得了一時,躲不了一世,出來混,遲早要還的。(誤)

Javascript RegExp Behavior

擺了一個烏龍,本來要寫一段Javascript測試日期格式是否有效,用RegExp做檢測,結果誤加了一個g參數,發現好玩的事:

var s = "2009/03/10";
var regex=/^\d{4}[/]\d{2}[/]\d{2}$/g;
alert(regex.test(s));
alert(regex.test(s));
alert(regex.test(s));
alert(regex.test(s));

以上程式碼,會出現一個true,一個false,再一個true,再一個false。如果拿掉g則可以如預期得到四個true。

如果我把測試放進迴圈中,如下:

for (var i=0; i<4; i++)
{
  var s = "2009/03/10";
  var regex=/^\d{4}[/]\d{2}[/]\d{2}$/g;
  alert(regex.test(s));
}

更有趣的事發生了,在IE/Safari中會得到四個true,而在Firefox/Chrome中則是true/false/true/false。

微軟技術的開發細節,盡在MSDN Library中,從沒費過心思去苦查狂找。但在Javascript上,雖然已用了多年,我卻連本書都沒買過,一向都是參考別人的Code亂抄一通、有了Google後更是順手一查,看到什麼吞什麼。RegExp的這個行為看來是By Design,但我不知要由何處查得權威性的定義。這回,決定再用一下Stackoverflow,不愧是國際級的平台,不到一個小時就有高手現身相救。

原來,使用g(Global)參數時,RegExp在做test()或exec()時,會保存一個指標lastIndex,記錄上次查詢結束的位置。所以,第一次test()後,lastIndex就指向字串結尾,第二次test()由結尾開始查,啥都查不到,就傳回false,然後再把lastIndex重設為0。由以下程式碼可以驗證:

var re = /a/g;
var s = "a";
document.write(re.lastIndex);
document.write(re.test(s))
document.write(re.lastIndex);
document.write(re.test(s))
document.write(re.lastIndex);

至於各瀏覽器的結果不同,我認為與Firefox/Chrome在重新宣告RegExp時,還沿用了前次的lastIndex有關(也許是基於效能,重覆使用物件)。利用以下程式,在IE可得00,而FF中則是01,故得證。

for (var i = 0; i < 2; i++)
{
  var re = /a/g;
  document.write(re.lastIndex);
  re.test("a");
}

這是一個標準的RTFM案例,不過我早先的困難在於無FM可R,感謝Stackoverflow的高手,回答問題時也順便指點我一個文件寶庫--Mozilla Developer Center,幾乎是網頁開發的四庫全書,當然不乏Javascript的完整參考,以後有問題就不用胡亂搜索了。

更多文章 下一頁 »

搜尋

Go

<March 2009>
SunMonTueWedThuFriSat
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234
 
RSS
【工商服務】
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


BlogLook Score and Rank

Syndication