javascript
JavaScript快速入门(四)——JavaScript函数
函數聲明
之前說的三種函數聲明中(參見JavaScript快速入門(二)——JavaScript變量),使用Function構造函數的聲明方法比較少見,我們暫時不提。function func() { }和var func = function() { }除了在聲明提升中有所不同之外也沒有其他不同,我們合并起來一起看。我們在這里著重講一個東西——匿名函數。
匿名函數顧名思義,就是沒有名字的函數。它的形式就是function() { }。請注意和之前說的兩種方式的區別,這里并沒有賦值給任何變量,也就是說,沒有指向這個函數的引用,我們無法在其他地方調用這個函數,也就是說,這種函數的使用價值只有一次。當我們把匿名函數賦值給其他變量時,就變成了var func = function() { }。是不是很熟悉?沒錯,就是我們之前說的變量聲明的方法。而如果func是隱式聲明的話,那么,這個函數就變成了全局函數。
匿名函數使用非常廣泛,它常用于只執行一次的函數,例如排序函數,我們可以這樣來寫:
?
var a = [2,1,4,7,5]; a.sort(function(num1, num2) {return num1 > num2; })我們傳了個匿名函數作為參數,因為這個函數只適用于這個地方,而無法用在其他地方。但如果是類似的地方也用了類似的函數,例如我們需要兩次排序:
?
?
var a = [2,1,4,7,5],b = [4,2,6,4,1]; function sortDesc(num1, num2) {return num1 > num2; } a.sort(sortDesc); b.sort(sortDesc);我們就可以把這個匿名函數剝離出來賦給一個function類型的變量,達到重復利用的目的。
?
由上面這個例子我們可以看出匿名函數的優劣。壞處很明顯,就是無法再次利用;好處是減少了聲明的消耗(當然,如果有兩次以上的利用的話,當然是聲明的消耗更少)。
?
函數調用
函數的調用和C中差不多,但形式可能有點不同。JavaScript的函數調用形式為:(函數)(參數列表)或者函數名(參數列表)。后者和C是一樣的,但前者和C是迥然不同的,因為C中函數聲明和函數調用是區分開的。先來看下例子: function add(num1, num2) {return num1 + num2; } var a = add(1, 2); // 3 這種方式就是函數名(參數列表)的形式,我們在C中經常見到,就不詳細說了,我們可以把上面那個換種形式來展現: var a = (function add(num1, num2) {return num1 + num2; })(1, 2); console.log(a); // 3 這樣我們就能實現聲明和執行一塊處理。 但是這樣有個問題,我們再看一種情況: var a = (function add(num1, num2) {return num1 + num2; })(1, 2); var b = add(1, 2); // error,add is not defined console.log(a); // 3 console.log(b); // undefined 這是因為add這個變量的作用域僅限于括號內,這個在之后的作用域講解中將講到。 這樣的調用,一般是在匿名函數中,為了讓函數立即執行才使用這種方式,又或者,利用它的不足,利用JavaScript的作用域特點,將函數內的變量全部轉為局部變量,達到封裝和防止污染全局的目的。函數嵌套
JavaScript的函數理論上是可以無限嵌套的,當然并不推薦嵌套太多,原因有很多,無論是性能還是代碼簡潔要求,都要求不應該嵌套太多。我們舉一個嵌套的例子: function getAbs(num) {function isNegative(num) {return num < 0;}return isNegative(num) ? -num : num; } var a = getAbs(-1); // 1 記住一句話,有嵌套就有父子關系(相互嵌套的不在參考范圍內,也極度不推薦)。在上面的例子中,父函數即為getAbs,子函數為isNegative。 在JavaScript的嵌套中,涉及到作用域的問題,我們先不講太復雜的,簡單的可以記成:父函數中聲明的所有變量,或者說,父函數中能使用的變量,都能在子函數中使用,但反過來,子函數中顯式聲明的所有變量,都不能在父函數中使用。下面會講到caller和callee來幫助理解嵌套。arguments對象
函數中,有一個默認的對象,不需要你去聲明,也不需要你去賦值,它叫做arguments,它是一個數組,保存著參數列表。先來看一個例子: function add(num1, num2) {console.log(arguments); // [1, 2]return num1 + num2; }; var b = add(1, 2); 注意,arguments對象保存的是實參。接下來,我們要展示JavaScript中非常有意思的一個東西,也是JavaScript靈活性的一大體現。在這之前,我們先來談下C中的函數重載。 維基中的定義為:函數重載(Function overloading),是Ada、C++、C#、D和Java等編程語言中具有的一項特性,這項特性允許創建數項名稱相同但功能的輸入輸出類型不同的子程序,它可以簡單地稱為一個單獨功能可以執行多項任務的能力。 在函數重載中,輸出類型可相同可不同,但參數列表一定要不一樣,可以是數量不一樣或者類型不一樣,或者兩者都不一樣。 但在JavaScript這類弱類型語言中,類型無法預定義,即輸入和輸出類型無法從函數定義看出來。那么只剩一項了,參數列表的長度,即參數數量。但這真的有影響嗎? 實際上,JavaScript沒有函數重載,實參比形參長的后果僅僅是沒有給實參一個別名而已。不懂?我們來看下例子: function add(num1, num2) {console.log(arguments); // [1, 2, 3]return num1 + num2 + arguments[2]; }; var b = add(1, 2, 3); // 6 我們可以巧妙的利用arguments對象,來達到我們的目的。我們甚至可以對上面的做個擴展,讓它能把所有參數的和返回,即使形參列表為空。 function add() {var sum = 0;for(var count = 0, length = arguments.length; count < length; count++) {sum += arguments[count];}return sum; }; var b = add(1, 2, 3, 4); // 10 那如果相反,形參列表長度比實參列表長呢? function add(num1, num2) {console.log(num1); // 1console.log(num2); // undefinedreturn num1 + num2; }; var b = add(1); // NaN 我們可以看到,超出實參長度的形參部分,就會是undefined,從而返回我們并不想要的結果(NaN表示應該是個number類型結果卻是其他類型)。我們可以稍作修改: function add(num1, num2) {num2 = num2 || 0;return num1 + num2; }; var b = add(1); // 1 利用邏輯操作符的特性來將形參實例化,保證使用時形參不為undefined。當然,這樣也有個別問題,如果傳入的實參邏輯值也是false(例如0、undefined、null)等等,我們就需要用全等符號進行判斷了,在此例中不做要求。caller和callee
這兩個對象,是用于判斷函數調用和執行的對象函數的。其中,arguments.callee返回當前正在執行的函數,func.caller返回函數的調用體所在函數。而arguments.caller永遠返回undefined。如果調用函數是在全局進行,那么func.caller將返回null。注意,在嚴格模式下這兩個對象將被禁用。 我們舉剛才的一個代碼為例: function getAbs(num) {function isNegative(num) {console.log(isNegative.caller); // getAbsconsole.log(arguments.callee); // isNegativereturn num < 0;}return isNegative(num) ? -num : num; } var a = getAbs(-1); 你可以將這段代碼運行一下,會發現,arguments.callee永遠指向函數本身,而函數名.caller將指向調用該函數的代碼所在函數,例如本例中即為getAbs。不過如果通過函數名.caller來尋找的話,耦合度太高。我們可以把兩個結合起來, function getAbs(num) {function isNegative(num) {console.log(arguments.callee)console.log(arguments.callee.caller)return num < 0;}return isNegative(num) ? -num : num; } var a = getAbs(-1); 有人問這個有什么用?這個嚴格的來說不是太有用,而且其安全性有問題,否則嚴格模式也不會禁用掉這兩個對象了。但說沒用也是不可能的,要不然也不會出現這兩個東西了。 首先,這個在調試的時候特別有效,可以幫我們理清代碼執行順序,或者尋找bug; 其次,可以用這兩個變量實現一些花哨的技巧,例如我們實現斐波那契數,正常做法是這樣: function fib(num) {if(num == 1 || num == 2) {return 1;}return fib(num - 1) + fib(num - 2); } var b = fib(6); // 8 但是這樣的壞處在于我們如果要更改個函數名,我們將同時修改三個地方(調用的暫時不論)。我們可以用我們剛學到的東西來解決這個問題: function fib(num) {if(num == 1 || num == 2) {return 1;}return arguments.callee(num - 1) + arguments.callee(num - 2); } var b = fib(6); // 8 但是,投機取巧也是有其弊端的,這會讓別人在看你的代碼的時候很費勁。用不用,取決于具體情況。轉載于:https://www.cnblogs.com/smght/p/4369549.html
總結
以上是生活随笔為你收集整理的JavaScript快速入门(四)——JavaScript函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机缩写术语完全介绍
- 下一篇: hadoop 单节点安装