1. 程式人生 > >javascript特性雜談

javascript特性雜談

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

最近語言學習有些瘋狂, 從Ruby到Lisp, 然後是C#, 既然已經瘋狂了, 就順面學習一下javascript吧. 對javascript的印象一直不佳, 從罵髒話最多的使用者, 到使用者平反的世界上

最被誤解的語言, 從所謂的讓人抓狂的特性, 到世界上任何可以用javascript實現的東西, 最終都會被javascript實現, 並且, 這是最後一個實現. 出處太多, 不一一列舉, 知者已知, 不知者也沒有必要為了這些無聊的言論特意找出處了.
其實也不是完全沒有用過javascript, 以前在開發一個Unity專案的時候用過一下Unity裡面的javascript, 只不過那個javascript我甚至都只能稱之為UnityScript. 太多太多自己實現的特性, 而又有些不夠完整. 現在, 認識一下真正的javascript吧.

環境

Mac OS X 10.8.2, node v0.8.16
需要解釋一下, node跟瀏覽器裡嵌入的javascript不一樣, 不具有類似confirmprompt等介面, 我用console.log來輸出.

概要

JavaScript本身就是設計為一個前端語言, 據說設計只用了10天, 有些缺陷, 但是的確足夠簡單. 雖然JavaScript The Definitive Guide和大多數的語言書籍一樣厚如磚頭, 但是其實語言本身的介紹只有前面近200頁, 這個厚度其實也就和R&D中描述的C語言差不多.
也就是因為設計比較簡單, JavaScript也被一些人認為不算是現代語言, 不具有現代語言的一些特性.

語法細節

  1. 可選的語句結束符;, 這個很少見. 不過一般的規範都推薦不要真的省.
  2. 支援自增++,自減符號--, 相對Ruby, Python來說, 這個要更習慣.
  3. switch和傳統的C語言語法類似, 但是可以支援字串的case.
  4. 支援NaN, null, undefined這三種表示類似無意義的量的方式, 有的時候這是混亂的根源. 也許還要再加上Infinity.
  5. 與大部分語言一樣, javascript也分為原生型別和引用型別, 其中原生型別在拷貝, 引數傳遞和比較時時通過值的方式, 而引用型別都是通過引用的方式.
  6. 字串為不可變型別, 任何的改變處理都是生成新字串. 比較時為值比較. 字串的值比較我個人認為時更加自然的做法, 比Java那種變態的方式要自然的多. 你幾乎要反覆的告訴每一個新來的程式設計師, 字串的值比較在java中要使用equals函式.
  7. 動態型別語言, 變數通過var定義.
  8. 支援用label方式的break和continue, 用於在多層迴圈中直接對外層迴圈進行break和continue.
  9. 完整並且傳統的try, catch, finally異常機制. 除了C++沒有finally不夠完整以外, 幾乎所有現在語言的異常都是這麼設計的了.

字串

javascript雖然說語法是類C的, 但是起點是Java, 所以儘管設計的面向物件系統雖然不是傳統的模版式的, 但是javascript中的字串都是物件.

"hello, world".length// out: 12

上述的程式碼在現在已經不稀奇了, 但是相對C++來說還是更先進的.(可見C++多落後了)

var str = "hello" + "," + "world!";console.log(str);// out: hello,world!

字串支援+操作符作為字串連線.

javascript有個奇怪的地方是字串和數字同時使用時:

console.log("3" + 4 + 5);// out: 345console.log(4 + 5 + "3");// out: 93

也就是說, 相對一些語言(比如php)會自動的將字串轉為數字來說, javascript是傾向於將數字轉為字串的. 其實因為這種用法過於靈活, 即使是Ruby和Python這樣以靈活著稱的語言都是不允許這樣的自動型別轉換的.
更詭異的還不只這些, 對於加法來說是如此, 對於乘法來說又是另外一回事:

console.log("3" * 4);// out: 12console.log("3" * "4");// out: 12

在乘法運算中, 因為javascript的字串並沒有像Ruby, Python一樣對乘法的運算做出特殊解釋(字串的乘法表示重複), 所以預設會將字串轉為整數進行運算, 更詭異的是, 就算是兩個字串, 同樣也會不報錯的進行整數轉換並且運算.

函式

函式在javascript中是第一類值, 同時還支援閉包. 這是javascript構成物件的基礎.

function add(x, y) {  return x + y;}var sub = function(x, y) {  return x - y;}add(2, 3);sub(5, 3);// out: 5// out: 2

有上述兩種函式構造形式, 在呼叫時沒有區別. 其中第一種方法和傳統的函式定義方式一樣, 而第二種實際上就是匿名函式的定義方式了. 只不過因為javascript中函式是第一類值, 所以可以很方便的賦值.

匿名函式

匿名函式也被稱為lambda, 是個很方便和有用的特性, 加上對閉包的支援, 以此衍生了很多特性. 也因此成就了javascript類函式語言的特性.

var caller = function(fun, leftParam, rightParam) {  fun(leftParam, rightParam);}caller(function(a, b) { console.log(a+b); }, 10, 20);// out: 30

如上例所示, 匿名函式很重要的一個應用就是用於很方便的構建高階函式. 也許上例有些太生造, 最常用的一個特性可能就是排序了, 因為排序的規則可能很多, 一般排序函式都允許再傳入一個函式作為引數, 來指定排序的規則. 比如再javascript中, 普通的排序函式有些奇怪, 預設是按照字串排序的. 見下例:

a = [1, 3, 2, 10, 20];console.log(a.sort());// out: [ 1, 10, 2, 20, 3 ]

這在大部分時候估計都不是我們要的做法, 預設這樣子我是第一次看見, 這就像字串和整數想加最後變成字串一樣詭異, 也許javascript本身設計的時候是作為前端檢驗表單啥為主的語言, 所以對字串這麼偏愛吧. 幸運的是, sort函式還是可以傳入一個函式作為排序規則的. 見下例:

a = [1, 3, 2, 10, 20];console.log( a.sort( function(a, b) { return a - b; } ) );// out: [ 1, 2, 3, 10, 20 ]

因為匿名函式和遞迴在javascript中使用的都比一般語言要多, 所以提供了arguments.callee用於表示當前呼叫的函式, 以方便匿名函式的遞迴呼叫, 事實上, 相對一般用函式名的遞迴呼叫方式, 這種方式要更加符合DRY(Dont Repeat Yourself)原則, 因為當函式名更改的時候, 不用再更改遞迴呼叫的函式名了.

var factorial = function(n) {  if (n <= 1) {    return 1;  }  else {    return n * arguments.callee(n - 1);  }}factorial(4);// out: 24

更有意思的是, arguments.callee在javascript的嚴格模式中是禁止的, 簡單的說就是這種呼叫方法是官方不推薦使用的錯誤用法, 在將來甚至有可能廢除, mozilla的解釋是這種更DRY的用例本身很”weak”, 但是卻阻止了inline優化的進行, 因為這種方式是通過引用un-inlined函式實現的, 也只有函式un-inlined時, arguments.callee才可以引用到.
事實上, 我覺得這簡直是因噎廢食的做法, 因為現在雖然是這樣實現的, 但是完全可以通過更好的語法分析, 然後進行編譯器的優化, 而不是因此廢棄這樣有用的語法. 這種用法絕對不像是官方說的那麼”weak”, 要知道, DRY幾乎是軟體設計領域頭等重要的原則.

閉包

一個閉包就是一個函式和被建立的函式中的範圍物件的組合. 因為閉包的強大特性和帶來的方便, 很多傳統的語言都逐漸了加入了對其的支援, 很多時候, 甚至被視為一個語言是否還算是跟上時代的標誌.

function makeIncrementor(base) {  var count = base;  return function(num) {    count += num;    return count;  }}obj1 = makeIncrementor(10);obj2 = makeIncrementor(20);obj1(1);// out: 11obj1(1);// out: 12obj2(2);// out: 22obj2(2);// out: 24

上面的例子較好的展示了閉包的特性, 可以獲得上層函式的引數和變數, 並且各自互相獨立, 因為閉包對區域性狀態的儲存, 很多時候能當作一個物件來使用.

靈活的引數呼叫

function add(x, y) {  return x + y;}add(2, 3, 4);add();add(2);// out: 5// out: NaN// out: NaN

上述程式碼在呼叫時不會發生錯誤, 而是直接把後面的引數拋棄掉.
甚至於, 後面的兩個引數不夠的函式呼叫, 會返回NaN, 也不會發生錯誤.
本質上是因為一旦函式呼叫引數不夠時, 後面的引數都會被置為undefined. 所以雖然javascript不支援預設引數, 但是可以模擬出來.

function mul(x, y) {  if (y === undefined) {    return x * 10;  }  return x * y;}mul(10);// out:  100

更靈活的語法是可以通過arguments變數來獲取引數, 這樣可以支援任意數量的函式引數.

function add() {    var sum = 0;    for (var i = 0, j = arguments.length; i < j; i++) {        sum += arguments[

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述