悟透JavaScript (1)

來源:互聯網
上載者:User

引子</p><p>  編程世界裡只存在兩種基本元素,一個是資料,一個是代碼。編程世界就是在資料和代碼千絲萬縷的糾纏中呈現出無限的生機和活力。</p><p>  資料天生就是文靜的,總想保持自己固有的本色;而代碼卻天生活潑,總想改變這個世界。</p><p>  你看,資料代碼間的關係與物質能量間的關係有著驚人的相似。資料也是有慣性的,如果沒有代碼來施加外力,她總保持自己原來的狀態。而代碼就象能量,他存在的唯一目的,就是要努力改變資料原來的狀態。在代碼改變資料的同時,也會因為資料的抗拒而反過來影響或改變代碼原有的趨勢。甚至在某些情況下,資料可以轉變為代碼,而代碼卻又有可能被轉變為資料,或許還存在一個類似E=MC2形式的數位轉換方程呢。然而,就是在資料和代碼間這種即矛盾又統一的運轉中,總能體現出電腦世界的規律,這些規律正是我們編寫的程式邏輯。</p><p>  不過,由於不同程式員有著不同的世界觀,這些資料和代碼看起來也就不盡相同。於是,不同世界觀的程式員們運用各自的方法論,推動著編程世界的進化和發展。</p><p>  眾所周知,當今最流行的編程思想莫過於物件導向編程的思想。為什麼物件導向的思想能迅速風靡編程世界呢?因為物件導向的思想首次把資料和代碼結合成統一體,並以一個簡單的對象概念呈現給編程者。這一下子就將原來那些雜亂的演算法與子程式,以及糾纏不清的複雜資料結構,劃分成清晰而有序的對象結構,從而理清了資料與代碼在我們心中那團亂麻般的結。我們又可以有一個更清晰的思維,在另一個思想高度上去探索更加浩瀚的編程世界了。</p><p>  在五祖弘忍講授完《對象真經》之後的一天,他對眾弟子們說:“經已講完,想必爾等應該有所感悟,請各自寫個偈子來看”。大弟子神秀是被大家公認為悟性最高的師兄,他的偈子寫道:“身是對象樹,心如類般明。朝朝勤拂拭,莫讓惹塵埃!”。此偈一出,立即引起師兄弟們的轟動,大家都說寫得太好了。只有火頭僧慧能看後,輕輕地歎了口氣,又隨手在牆上寫道:“對象本無根,類型亦無形。本來無一物,何處惹塵埃?”。然後搖了搖頭,揚長而去。大家看了慧能的偈子都說:“寫的什麼亂七八糟的啊,看不懂”。師父弘忍看了神秀的詩偈也點頭稱讚,再看慧能的詩偈之後默然搖頭。就在當天夜裡,弘忍卻悄悄把慧能叫到自己的禪房,將珍藏多年的軟體真經傳授於他,然後讓他趁著月色連夜逃走...</p><p>  後來,慧能果然不負師父厚望,在南方開創了禪宗另一個廣闊的天空。而慧能當年帶走的軟體真經中就有一本是《JavaScript真經》!</p><p>  迴歸簡單</p><p>  要理解JavaScript,你得首先放下對象和類的概念,回到資料和代碼的本原。前面說過,編程世界只有資料和代碼兩種基本元素,而這兩種元素又有著糾纏不清的關係。JavaScript就是把資料和代碼都簡化到最原始的程度。</p><p>  JavaScript中的資料很簡潔的。簡單資料只有 undefined, null, boolean, number和string這五種,而複雜資料只有一種,即object。這就好比中國古典的樸素唯物思想,把世界最基本的元素歸為金木水火土,其他複雜的物質都是由這五種基本元素組成。</p><p>  JavaScript中的代碼只體現為一種形式,就是function。</p><p>  注意:以上單詞都是小寫,不要和Number, String, Object, Function等JavaScript內建函數混淆了。要知道,JavaScript語言是區分大小寫呀!</p><p>  任何一個JavaScript的標識、常量、變數和參數都只是unfined, null, bool, number, string, object 和 function類型中的一種,也就typeof傳回值表明的類型。除此之外沒有其他類型了。</p><p>  先說說單一資料型別吧。</p><p>  undefined: 代表一切未知的事物,啥都沒有,無法想象,代碼也就更無法去處理了。</p><p>  注意:typeof(undefined) 返回也是 undefined。</p><p>  可以將undefined賦值給任何變數或屬性,但並不意味了清除了該變數,反而會因此多了一個屬性。</p><p>  null: 有那麼一個概念,但沒有東西。無中似有,有中還無。雖難以想象,但已經可以用代碼來處理了。</p><p>  注意:typeof(null)返回object,但null並非object,具有null值的變數也並非object。</p><p>  boolean: 是就是,非就非,沒有疑義。對就對,錯就錯,絕對明確。既能被代碼處理,也可以控制碼的流程。</p><p>  number: 線性事物,大小和次序分明,多而不亂。便於代碼進行批量處理,也控制碼的迭代和迴圈等。</p><p>  注意:typeof(NaN)和typeof(Infinity)都返回number 。</p><p>  NaN參與任何數值計算的結構都是NaN,而且 NaN != NaN 。</p><p>  Infinity / Infinity = NaN 。</p><p>  string: 面向人類的理性事物,而不是機器訊號。人機資訊溝通,代碼據此理解人的意圖等等,都靠它了。</p><p>  簡單類型都不是對象,JavaScript沒有將對象化的能力賦予這些簡單類型。直接被賦予簡單類型常量值的標識符、變數和參數都不是一個對象。</p><p>  所謂“對象化”,就是可以將資料和程式碼群組織成複雜結構的能力。JavaScript中只有object類型和function類型提供了對象化的能力。</p><p>  沒有類</p><p>  object就是對象的類型。在JavaScript中不管多麼複雜的資料和代碼,都可以組織成object形式的對象。</p><p>  但JavaScript卻沒有 “類”的概念!</p><p>  對於許多物件導向的程式員來說,這恐怕是JavaScript中最難以理解的地方。是啊,幾乎任何講物件導向的書中,第一個要講的就是“類”的概念,這可是物件導向的支柱。這突然沒有了“類”,我們就象一下子沒了精神支柱,感到六神無主。看來,要放下對象和類,達到“對象本無根,類型亦無形”的境界確實是件不容易的事情啊。</p><p>這樣,我們先來看一段JavaScript程式:</p><p>  var life = {};</p><p>  for(life.age = 1; life.age <= 3; life.age++)</p><p>  {</p><p>  switch(life.age)</p><p>  {</p><p>  case 1: life.body = "卵細胞";</p><p>  life.say = function(){alert(this.age+this.body)};</p><p>  break;</p><p>  case 2: life.tail = "尾巴";</p><p>  life.gill = "腮";</p><p>  life.body = "蝌蚪";</p><p>  life.say = function(){alert(this.age+this.body+"-"+this.tail+","+this.gill)};</p><p>  break;</p><p>  case 3: delete life.tail;</p><p>  delete life.gill;</p><p>  life.legs = "四條腿";</p><p>  life.lung = "肺";</p><p>  life.body = "青蛙";</p><p>  life.say = function(){alert(this.age+this.body+"-"+this.legs+","+this.lung)};</p><p>  break;</p><p>  };</p><p>  life.say();</p><p>  };</p><p>  這段JavaScript程式一開始產生了一個生命對象life,life誕生時只是一個光溜溜的對象,沒有任何屬性和方法。在第一次生命過程中,它有了一個身體屬性body,並有了一個say方法,看起來是一個“卵細胞”。在第二次生命過程中,它又長出了“尾巴”和“腮”,有了tail和gill屬性,顯然它是一個“蝌蚪”。在第三次生命過程中,它的tail和gill屬性消失了,但又長出了“四條腿”和“肺”,有了legs和lung屬性,從而最終變成了“青蛙”。如果,你的想像力豐富的話,或許還能讓它變成英俊的“王子”,娶個美麗的“公主”什麼的。不過,在看完這段程式之後,請你思考一個問題:</p><p>  我們一定需要類嗎?</p><p>  還記得兒時那個“小蝌蚪找媽媽”的童話嗎?也許就在昨天晚,你的孩子剛好是在這個美麗的童話中進入夢鄉的吧。可愛的小蝌蚪也就是在其自身類型不斷演化過程中,逐漸層成了和媽媽一樣的“類”,從而找到了自己的媽媽。這個童話故事中蘊含的編程哲理就是:對象的“類”是從無到有,又不斷演化,最終又消失於無形之中的...</p><p>  “類”,的確可以協助我們理解複雜的現實世界,這紛亂的現實世界也的確需要進行分類。但如果我們的思想被“類”束縛住了,“類”也就變成了“累”。想象一下,如果一個生命對象開始的時就被規定了固定的“類”,那麼它還能演化嗎?蝌蚪還能變成青蛙嗎?還可以給孩子們講小蝌蚪找媽媽的故事嗎?</p><p>  所以,JavaScript中沒有“類”,類已化於無形,與對象融為一體。正是由於放下了“類”這個概念,JavaScript的對象才有了其他程式設計語言所沒有的活力。</p><p>  如果,此時你的內心深處開始有所感悟,那麼你已經逐漸開始理解JavaScript的禪機了。</p><p>  函數的魔力</p><p>  接下來,我們再討論一下JavaScript函數的魔力吧。</p><p>  JavaScript的代碼就只有function一種形式,function就是函數的類型。也許其他程式設計語言還有procedure或 method等代碼概念,但在JavaScript裡只有function一種形式。當我們寫下一個函數的時候,只不過是建立了一個function類型的實體而已。請看下面的程式:</p><p>  function myfunc()</p><p>  {</p><p>  alert("hello");</p><p>  };</p><p>  alert(typeof(myfunc));</p><p>  這個代碼運行之後可以看到typeof(myfunc)返回的是function。以上的函數寫法我們稱之為“定義式”的,如果我們將其改寫成下面的“變數式”的,就更容易理解了:</p><p>  var myfunc = function ()</p><p>  {</p><p>  alert("hello");</p><p>  };</p><p>  alert(typeof(myfunc));</p><p>  這裡明確定義了一個變數myfunc,它的初始值被賦予了一個function的實體。因此,typeof(myfunc)返回的也是function。其實,這兩種函數的寫法是等價的,除了一點細微差別,其內部實現完全相同。也就是說,我們寫的這些JavaScript函數只是一個命了名的變數而已,其變數類型即為function,變數的值就是我們編寫的函數代碼體。</p><p>  聰明的你或許立即會進一步的追問:既然函數只是變數,那麼變數就可以被隨意賦值並用到任意地方囉?<br />我們來看看下面的代碼:</p><p>  var myfunc = function ()</p><p>  {</p><p>  alert("hello");</p><p>  };</p><p>  myfunc(); //第一次調用myfunc,輸出hello</p><p>  myfunc = function ()</p><p>  {</p><p>  alert("yeah");</p><p>  };</p><p>  myfunc(); //第二次調用myfunc,將輸出yeah</p><p>  這個程式啟動並執行結果告訴我們:答案是肯定的!在第一次調用函數之後,函數變數又被賦予了新的函數代碼體,使得第二次調用該函數時,出現了不同的輸出。</p><p>  好了,我們又來把上面的代碼改成第一種定義式的函數形式:</p><p>  function myfunc ()</p><p>  {</p><p>  alert("hello");</p><p>  };</p><p>  myfunc(); //這裡調用myfunc,輸出yeah而不是hello</p><p>  function myfunc ()</p><p>  {</p><p>  alert("yeah");</p><p>  };</p><p>  myfunc(); //這裡調用myfunc,當然輸出yeah</p><p>  按理說,兩個簽名完全相同的函數,在其他程式設計語言中應該是非法的。但在JavaScript中,這沒錯。不過,程式運行之後卻發現一個奇怪的現象:兩次調用都只是最後那個函數裡輸出的值!顯然第一個函數沒有起到任何作用。這又是為什麼呢?</p><p>  原來,JavaScript執行引擎並非一行一行地分析和執行程式,而是一段一段地分析執行的。而且,在同一段程式的分析執行中,定義式的函數語句會被提取出來優先執行。函數定義執行完之後,才會按順序執行其他語句代碼。也就是說,在第一次調用myfunc之前,第一個函數語句定義的代碼邏輯,已被第二個函數定義語句覆蓋了。所以,兩次都調用都是執行最後一個函數邏輯了。</p><p>  如果把這個JavaScript代碼分成兩段,例如將它們寫在一個html中,並用<script/>標籤將其分成這樣的兩塊:</p><p>  <script></p><p>  function myfunc ()</p><p>  {</p><p>  alert("hello");</p><p>  };</p><p>  myfunc(); //這裡調用myfunc,輸出hello</p><p>  </script></p><p>  <script></p><p>  function myfunc ()</p><p>  {</p><p>  alert("yeah");</p><p>  };</p><p>  myfunc(); //這裡調用myfunc,輸出yeah</p><p>  </script></p><p>  這時,輸出才是各自按順序來的,也證明了JavaScript的確是一段段地執行的。</p><p>  一段代碼中的定義式函數語句會優先執行,這似乎有點象靜態語言的編譯概念。所以,這一特徵也被有些人稱為:JavaScript的“先行編譯”。</p><p>  大多數情況下,我們也沒有必要去糾纏這些細節問題。只要你記住一點:JavaScript裡的代碼也是一種資料,同樣可以被任意賦值和修改的,而它的值就是代碼的邏輯。只是,與一般資料不同的是,函數是可以被調用執行的。</p><p>  不過,如果JavaScript函數僅僅只有這點道行的話,這與C++的函數指標,DELPHI的方法指標,C#的委託相比,又有啥稀奇嘛!然而,JavaScript函數的神奇之處還體現在另外兩個方面:一是函數function類型本身也具有對象化的能力,二是函數function與對象 object超然的結合能力。</p><p>

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.