前言:無所不能的JavaScript JavaScript起源於Netscape公司的LiveScript語言,這是一種基於對象和事件驅動的客戶端腳本語言,最初的設計是為了檢驗HTML表單輸入的正確性,只是用於網頁開發的一個弱類型腳本語言。隨著Html5在PC和移動端越來越流行,JavaScript ...
前言:無所不能的JavaScript
JavaScript起源於Netscape公司的LiveScript語言,這是一種基於對象和事件驅動的客戶端腳本語言,最初的設計是為了檢驗HTML表單輸入的正確性,只是用於網頁開發的一個弱類型腳本語言。隨著Html5在PC和移動端越來越流行,JavaScript變得更加重要了,各種層出不窮的框架使得JavaScript的開發更加簡捷,V8的性能帶來了Node.js,將JavaScript 推向了伺服器端,同時還被用在桌面應用、游戲、AR等各種場景,JavaScript已經變成了全能型選手。時至今日,JavaScript可算是世界上最流行的編程語言之一,這個被大量的開發者與設計師隨手拈來增強他們的Web前端的腳本語言,越來越被重視。,
JavaScript成功在於它的無所不為。從一個小腳本到前、後端通吃,這足以證明瞭它的強大之處。
JavaScript一度被認為是一種玩具編程語言,它有很多缺陷,所以不被大多數後端開發人員所重視。很多人認為,相較Java、Ruby、Python、.Net等語言來說,寫JavaScript代碼很簡單,並且JavaScript只是為了在網頁上添加一點交互和動畫效果,認為只需要會使用jQuery 做一些頁面以及前後端交互就可以在簡歷寫上熟悉JavaScript。
但這是完全錯誤的理解。JavaScript確實很容易上手,學習性價比高,稍微掌握一點JS基礎知識,外加HTML和CSS,就可以做一個簡單網頁或者拿來應聘一份可以糊口的工作。但其精髓卻不為大多數開發人員所熟知,如果想精通JS,使用JS進行隨心所欲地編程,編寫高質量的JavaScript代碼更是難上加難。一個合格的開發人員應該精通JavaScript和其他編程語言。如果你已經掌握了其他編程語言,或者你還什麼都不會,請立刻開始學習JavaScript,不要被Web時代所淘汰。
函數的實參和形參
在Java 編程中,函數的形參(parameter)和實參(argument)概念屢見不鮮,但是對JavaScript而言,這兩個概念通常被弱化了。但其實在JS編程中,還是需要明確理解一下這兩個概念。
弱類型腳本語言JS,它的函數定義並未指定函數形參的類型,函數調用也未對傳入的實參值做任何類型檢查,對傳入參數個數也不檢查。
一句話來說,JavaScript 的實參和形參可以不一致。
JavaScript 的arguments 參數對象
嚴格來說,我們應該稱arguments 為“實參對象”,因為arguments 對象的長度是由實參個數而不是形參個數決定的。形參是函數內部重新開闢記憶體空間存儲的變數,但是其與arguments對象記憶體空間並不重疊。對於arguments和值都存在的情況下,兩者值是同步的,但是針對其中一個無值的情況下,對於此無值的情形值不會得以同步。
如下代碼可以得以驗證:
1 function test(arg1, arg2, arg3){ 2 console.log(arguments.length); // result: "2" 3 arg1 = 100; 4 console.log(arguments[0]); // result: "100" 5 arguments[0] = "200"; 6 console.log(arg1); // result: "200" 7 8 console.log(arg3); // result: "undefined" 9 arg3 = true; 10 console.log(arguments[2]); // result: "undefined" 11 console.log(arg3) // result: true 12 } 13 test(1, 2);
根據代碼我們可以得到JS實參對象的一些規則:
規則1:當傳入的實參比形參個數少的時候,剩下的形參都將設置為undefined;
規則2:利用arguments.length可以獲取調用函數的參數個數,它的值取決於調用處(實參),不一定等於形參個數;
規則3:需要使用可選實參的時候,需要將可選實參放在實參列表的最後,但應對這些可選形參加以控制,代碼如下:
1 function getName(param1, /* optional */ param2) { 2 if(param2 === undefined) param2 = ""; // 嚴格相等,防止null;不需要判斷空的話,param2 = param2 || "" 3 dosomething();4 }
arguments.callee 與函數形參
說了半天的實參,那麼形參在哪裡?先看arguments 對象的callee 屬性,callee 屬性是 arguments 對象的一個成員,它表示對函數對象本身的引用,這有利於匿名,該屬性僅當相關函數正在執行時才可用。
arguments.callee 得到函數本身,arguments.callee() 是調用自己,arguments.callee.length 是獲取形參個數(PS:當然,函數名.length 亦可)。
好,有了這些知識,我們能做什麼?
形參的作用無非就是在JS組件編程中,配合實參做一些校驗處理和程式分支處理。arguments.callee 的作用就大了,利用它可以在函數內部進行遞歸邏輯。
代碼如下:
1 function test(n){ 2 if(n>0){ 3 arguments.callee(n - 1); 4 alert(n); 5 } 6 } 7 var n = 5; 8 test(n);
如果知道結果是多少,那就OK了。
註:callee在嚴格模式下被禁用。
arguments 與類數組對象
arguments 對象是比較特別的一個對象,實際上是當前函數的一個內置屬性。arguments 非常類似Array,但實際上又不是一個Array實例。
我們稱呼這類對象為類數組對象。《JS權威指南》中提到過這一概念。
JS數組的一些特性是其他對象所沒有的:
1.當有新的元素加進來的時候,自動更新length屬性;
2.設置length為一個較小值將截斷數組;
3.從Array.prototype中繼承一些有用的方法;
4.其類屬性為”Array“;
這些屬性讓數組和常規對象有所區別,稱為”特殊的對象“。當然,這不是我們這邊要關心的,上面這些屬性並不是數組的本質特性。
一種完全合理的看法是:"把擁有一個數值length屬性和對應非負整數屬性的對象看做一種類型的數組,即類數組對象"。
這些類數組對象(參數對象arguments),並不能調用數組的方法或者期望length屬性有什麼特殊的行為,但是仍然可以類似數組那樣遍歷它們,並且可以使用call來操作數組的方法。
上一段代碼,驗證一下:
1 function ArgTest(a, b) 2 var numargs = arguments.length; 3 alert(numargs); //獲取實際傳遞參數長度 4 5 var expargs = ArgTest.length; 6 alert(expargs); // 獲取應該傳遞參數長度,使用 arguments.callee.length 可以獲得同樣的效果 7 8 alert(arguments.callee); //可以列印函數本身 9 10 Array.prototype.selfvalue = 1; 11 alert(new Array().selfvalue); 12 } 13 function testAguments(){ 14 alert(arguments.selfvalue); 15 }
運行代碼你會發現第一個alert顯示1,這表示數組對象擁有selfvalue屬性,值為1,而當你調用函數testAguments時,你會發現顯示的是“undefined”,說明瞭不是arguments的屬性,即arguments並不是一個數組對象。
因此,直接調用arguments.slice() 將返回一個"Object doesn't support this property or method"錯誤,因為arguments不是一個真正的數組。
使用Array.prototype.slice.apply(arguments)能將函數的參數對象轉化為一個真正的數組。
JavaScript 的函數重載
對於arguments 參數說了這麼多,最後再扯一下函數重載吧。本人是Java 程式猿出身,對於面向對象的三大特性(封裝、繼承、多態)是根深蒂固,JavaScript的封裝和繼承在後續文章中會介紹到,那麼多態和重載,JavaScript是如何處理的呢?
很遺憾,由於JavaScript中函數的聲明和調用特性,可以看出JavaScript中函數是不能重載的。
根據其他語言中重載的依據:"函數返回值不同或形參個數不同",我們可以得出上述結論:
第一:Javascript函數的聲明是沒有返回值類型這一說法的;
第二:JavaScript中形參的個數嚴格意義上來講只是為了方便在函數中的變數操作,實際上實參已經存儲在arguments對象中了。
另外,從JavaScript函數本身深入理解為什麼JavaScript中函數是不能重載的:在JavaScript中,函數其實也是對象,函數名是關於函數的引用,或者說函數名本身就是變數。對於如下所示的函數聲明與函數表達式,其實含以上是一樣的(在不考慮函數聲明與函數表達式區別的前提下),非常有利於我們理解JavaScript中函數是不能重載的這一特性。
編後語
那麼問題來了,為什麼我們要學JavaScript?尤其是當你已經掌握了某些其他編程語言如Java、C++的情況下。
簡單粗暴的回答就是:因為你沒有選擇。在Web世界里,只有JavaScript能跨平臺、跨瀏覽器驅動網頁,與用戶交互。
本人工作從事後端開發,屬於一個“半吊子”的前端攻城獅,不會切圖,不會設計,CSS和HTML會一些,但這些絲毫不能影響我對JavaScript 熱愛。
JavaScript 和nodejs 的大前端時代即將來臨,不管你學不學,它就在那裡!