文章有代碼部分,如果有閱讀困難,請到原文閱讀:https://blog.jing.do/4212
前言:本篇文章是我在查詢時偶然間發現的,雖然年代久遠但是依舊非常適合入門學習,特此翻譯下分享給大家,順便給大家加了一些備注方便閱讀(特意加粗刷存在感)。原文全文和鏈接在最后
———————-以下是正文———————-
備注:本篇文章是34歲的程序員Ayman Hourieh在2006年發布的。我找到了他網站的鏈接,但是網站已經不存在了。我用Wayback Machine找到了文章的鏡像。為了讓更多人看到把他摘錄分享出來。我只做了一些鏈接的修改。
JavaScript是一個功能強大的面向對象語言。表面上看他他和JAVA和C非常相似,但是他卻截然不同,其核心在于JavaScript更像一個功能性語言。本篇文章是一些JavaScript的小提示,一部分提供一些類C語言的功能模擬,另一部分是想要提高性能并探討下腳本語言中一些比較難懂的東西。索引如下:
多用途的Array
棧
隊列
二叉樹
String Concatenation 對比 Array.join
給對象綁定方法
使用自定義排序
Assertion
靜態局部變量
null, undefined, and delete
深層嵌套
使用Firebug
$ 和 $$
console.log(format, obj1, …)
console.trace()
inspect(obj)
多用途的Array
盡管JavaScript在數據結構方面看起來很奇特,他的Array比其他編程語言(如C ++或Java)用途更加廣泛。 它通常用作數組或關聯數組,后面將演示如何將其用作堆棧,隊列和二叉樹。 復用Array而不是編寫其他的數據結構有兩個好處:首先,不用浪費時間去重寫已經存在的功能,其次,內置瀏覽器對JavaScript的運行將更高效。
棧
大家都知道棧是后進先出:最后插入的會被最先移除。array有兩個方法來實現棧的功能。他們是push()和pop()。push()用于插入item到array的尾部,而pop()則用于移除并返回最后一個item。以下是代碼的實例:
varstack=[];stack.push(2);// 當前棧是 [2]stack.push(5);// 當前棧是 [2, 5]vari=stack.pop();// 當前棧是 [2]alert(i);// 顯示 5
隊列
隊列是先進先出的:現行插入的將會被最先移除。array可以用push()和shift()來實現隊列。push()用于插入item到尾部,shift()用于移除第一個item。案例如下:
varqueue=[];queue.push(2);// 現在的隊列 [2]queue.push(5);// 現在的隊列 [2, 5]vari=queue.shift();// 現在的隊列 [5]alert(i);// 顯示 2
值得注意的是,array還有一個unshift()的函數。這個函數用于將item放到array的頭部。所以棧也可以使用unshift()/shift(),而隊列可以用unshift()/pop()。
如果這些函數名稱讓你迷茫了(譯者注:因為unshift()是處理頭部,所以相對應的棧需要從頭部出去,隊列需要換到尾部),你可以給他們取個別名,比如,創建隊列的方法名為add和remove:
varqueue=[];queue.add=queue.push;queue.remove=queue.shift;queue.add(1);vari=queue.remove();alert(i);
二叉樹
二叉樹是在樹的節點表示數據。每個節點有一個數據并且有兩個子節點(左叉樹和右叉樹)。在C語言里,這種數據結構通常使用指針來實現。這種實現方法也可以在JavaScript中使用對象和引用來實現。然而,對于一些小的二叉樹,有一種更簡單便捷的方法,array的第一個item作為樹的頭。 如果可以使用以下公式計算節點i,則索引左右節點:
leftChild(i)=2i+1rightChild(i)=2i+2
這張圖揭示了這個算法: (來自于Wikipedia):
正如你所看到的,這種方法并不是JavaScript的獨有之處,但是在處理小的二叉樹時非常有用。 例如,您可以編寫自己的函數來獲取和設置節點的值或子節點,并遍歷二叉樹,這些方法與做計算或for循環一樣簡單。但是,這種方法的缺點是隨著樹的深度增加,浪費的存儲空間越來越多。
String Concatenation 對比 Array.join
大家都知道,如果做太多的字符串鏈接會讓性能下降(譯者注:不知道的去補課),并且這個非常容易避免。如果你想要用各個字符來組成一個字符串,最差的方法是使用+把所有的字符結合到一起:
str='';for(/* each piece */){str+=piece;// bad for performance!}returnstr;
這種方法將使用太多的字符串鏈接,會讓性能枯竭。
在JavaScript中有個更好的辦法,就是Array.join(),他可以讓所有array內的元素連接成一個字符串:
vartmp=[];for(/* each piece */){tmp.push(piece);}str=tmp.join(' ');// 用空格作為分隔符returnstr;
該方法不會受到額外的字符串對象的影響,通常執行的非常高效
給對象綁定方法
任何使用JavaScript事件的人都可能遇到了一種情況,他們需要將對象的方法分配給事件處理程序。 這里的問題是事件處理程序會被HTML調用,即使它們最初被綁定到另一個對象。 為了解決這個問題,我用一個函數將對象和方法綁定; 它他會運行對象和方法,并返回一個在該對象調用該方法的函數。 我在Prototype中找到了一個竅門,并且寫了以下函數來在不包含Prototype的項目中使用它:
functionbind(obj,method){returnfunction(){returnmethod.apply(obj,arguments);}}
如何使用:
varobj={msg:'Name is',buildMessage:function(name){returnthis.msg+' '+name;}}alert(obj.buildMessage('John'));// displays: Name is Johnf=obj.buildMessage;alert(f('Smith'));// displays: undefined Smithg=bind(obj,obj.buildMessage);alert(g('Smith'));// displays: Name is Smith
使用自定義排序
排序是一個常見的工作。 JavaScript提供了一種排序數組的方法。 但是,該方法默認按字母順序排列 —— 非字符串元素在排序之前被強制轉換為字符串,這個使得數字排序會有意想不到的結果:
varlist=[5,10,2,1];list.sort()// list is now: [1, 10, 2, 5]
這個解釋很容易: 數字被強制轉換成了字符串,所以10編程了’10’而2變成了’2’,那么JavaScript對比兩個字符串的時候,先對比第一個字符。如果str1的第一個字符出現在字符集中的第一個字符之前,則str1被認為是“小于”str2。 在我們的情況下,’1’在’2’之前,所以’10’小于’2’。
幸運的是,JavaScript提供一個重寫機制,讓我們可以自定義如何排序,我們用a和b兩個元素最為例子:
如果a
如果a=b,返回0
如果a>b,返回大于0
寫這樣的程序比較容易:
functioncmp(a,b){returna-b;}
我們現在可以使用這個方法來做排序:
varlist=[5,10,2,1];list.sort(cmp);//listisnow:[1,2,5,10]
Array.sort()牛逼的地方是允許更復雜的排序。 假設你有一個論壇帖子,每個帖子看起來像:
varpost={id:1,author:'...',title:'...',body:'...'}
如果你想用id排序,只需要創建以下函數:
functionpostCmp(a,b){returna.id-b.id;}
可以說,使用瀏覽器的方法進行排序將比在JavaScript中實現排序函數更有效。 但是,數據應該在服務器端進行排序。所以,除非必要,否則不應該讓數據在客戶端排序。
Assertion
Assertion是一種常用的調試技術,它用于確保表達式在執行期間計算為真。 如果表達式計算為假,則表示代碼中可能出現的錯誤。 JavaScript缺少一個內置的Assertion功能,但幸運的是,它很容易編寫一個。 如果傳遞的表達式的計算結果為假,以下實現會拋出AssertException類型的異常:
functionAssertException(message){this.message=message;}AssertException.prototype.toString=function(){return'AssertException: '+this.message;}functionassert(exp,message){if(!exp){thrownewAssertException(message);}}
自己拋出異常并不是非常有用,但是當與有用的錯誤消息或調試工具結合使用時,您可以檢測到有問題的部分。
您還可以使用以下代碼段檢查異常是否為Assertion異常:
try{// ...}catch(e){if(einstanceofAssertException){// ...}}
該函數的使用類似于C或Java:
assert(obj!=null,'Object is null');
如果obj碰巧為null,Firefox將在JavaScript控制臺中打印以下消息:
uncaughtexception:AssertException:Objectisnull
Static Local Variables
大家知道,一些語言像C ++,他們有靜態變量的概念,用于函數調用。 JavaScript并不支持此技術。 然而,“功能也是對象”讓這個功能成為可能。 方法是:將靜態變量變為函數的屬性。 假設我們要創建一個計數器函數:
functioncount(){if(typeofcount.i=='undefined'){count.i=0;}returncount.i++;}
當第一次調用count時,count.i是未定義的,所以if條件為true,count.i為0。因為我們將變量存儲為一個函數屬性,它將在函數調用之間保留它的值, 因此它可以被認為是一個靜態變量。
這里有個性能提升小技巧:
functioncount(){returncount.i++;}count.i=0;
雖然第一個例子將Count的所有邏輯封裝在主體中,但第二個例子更為有效。
null, undefined, and delete
因為JavaScript有undefined和null所以他不同于其他語言。null是一個特別的數值,他表示沒有數值。null會被認為一個特別的對象,因為typeof null會返回null。
undefined表示變量沒有被定義,或者定義了但沒有給數值。以下情況都會顯示undefined:
// i is not declared anywhere in codealert(typeofi);vari;alert(typeofi);
雖然undefined和null是兩個不同的類型,但是如果使用 == ,會被判定為相等,但是如果是 === 則不等。
JavaScript還有一個刪除操作符,”undefines”一個屬性,可以將其應用于對象屬性和數組成員,使用var聲明的變量不能被刪除,而是隱式聲明(implicitly declared )的變量可以:
varobj={val:'Some string'}alert(obj.val);// displays 'Some string'deleteobj.val;alert(obj.val);// displays 'undefined'
深層嵌套
如果您需要在深層嵌套對象上執行多個操作,最好將其引用到臨時變量中,而不是每次對其進行解引用。 例如,假設您想在文本字段上執行一系列操作:
document.forms[0].elements[0]
建議您存儲到變量中,并使用此變量而不是以上構造:
varfield=document.forms[0].elements[0];// Use field in loop
每個點都導致一個操作來檢索一個屬性,在一個循環中,這些操作是相加的,所以最好做一次,將對象存儲在變量中并重新使用它。
Using Firebug
Firefox有一個非常棒的擴展,用于調試名為Firebug的JavaScript代碼。 它提供一個具有斷點和堆棧視圖的調試器,以及一個JavaScript控制臺。 它還可以監視Ajax請求。 此外,擴展提供了一組JavaScript函數和對象來簡化調試。 您可以在Firebug的文檔頁面中詳細研究它們。 這里有一些我覺得有用的:
$ and $$
熟悉Prototype的人馬上就認出他們了,
$() 接受一個字符串參數,并返回其ID是傳遞的字符串的DOM元素。(譯者注:Jquery)
$('nav')// returns the element whose id is #nav.
$$()返回DOM的數組
$$('div li.menu')// returns an array of li elements that are// located inside a div and has the .menu class
console.log(format, obj1, …)
console對象提供顯示log消息的方法,這個將比alert更加好用,console.log()有點像C里面的printf,他會將輸入轉化為字符串在console中展示:
vari=1;console.log('i value is %d',i);// prints:// i value is 3
console.trace()
此方法打印一個堆棧跟蹤調用它。 它不需要輸入參數
inspect(obj)
該功能需要一個參數。 它切換到檢查選項卡并檢查傳遞的對象。