JavaScript 語言核心(10)初探一級函式
JavaScript 語言核心(9)不可輕忽的函式基礎 << 前情 在 JavaScript 中,函式是物件,是 function max(num1, num2) { return num1 > num2 ? num1 : num2; } var maximum = max; console.log(max(10, 20)); // 20 console.log(maximum(10, 20)); // 20 注意,在將 var max = function(num1, num2) { return num1 > num2 ? num1 : num2; }; var maximum = max; console.log(max(10, 20)); // 20 console.log(maximum(10, 20)); // 20 函式就如同數值,可以指定給變數,函式與數值的地位相同,並不會像有些語言中,無法像數值一樣地被指定,不會淪為二等公民,因此,對於支持函式可如數值一樣指定給變數的語言,我們稱函式在這個語言中是一等(First-class)函式或一級函式。 上面你所看到的函式撰寫方式,稱之為函式實字(Function literal),這就像你寫下一個數值實字、物件實字或陣列實字,會產生數值或物件等: var number = 10; // Number literal var obj = {x : 10}; // Object literal var array = [1, 2, 3]; // Array literal var func = function() { // Function literal // do something... }; 函式實字會產生 var max = new Function('num1', 'num2', 'return num1 > num2 ? num1 : num2' ); var maximum = max; console.log(max(10, 20)); // 20 console.log(maximum(10, 20)); // 20 基本上,實務上很少會直接建立Function實例,以上只是表示,函式確實是Function實例。 既然函式是物件,它就可以任意指定給其他變數,也就可以指定作為另一個函式的引數,那它就不僅能被呼叫,還可以主動要求另一個函式執行所指定的函式內容。例如: var printIt = function(elem) { console.log(elem); }; [1, 2, 3].forEach(printIt); // 1 2 3 var naturalOrder = function(num1, num2) { return num1 - num2; }; [5, 1, 7, 3, 2].sort(naturalOrder) // 1 2 3 5 7 .forEach(printIt); 上例以
像這種將函式主動丟入函式作為引數,在 JavaScript 等具備一級函式的語言中,是很常見到的應用。事實上,若不需要名稱,你也可以如下: var numbers = [5, 1, 7, 3, 2]; numbers.sort(function(num1, num2) { // 1 2 3 5 7 return num1 - num2; }) .forEach(function(elem) { console.log(elem); }); 直接傳入函式很方便,不過函式名稱有時是必要的,像上面的可讀性並不會比較好。 你也可以從函式中傳回函式,這通常會形成閉包(Closure)綁定某些運算過後的資源,再傳回函式,這之後還會再談到應用。 以函式實字所建立的 (function() { console.log('匿名函式...'); })(); 實際上,函式實字也可以指定名稱。例如: var maximum = function max(num1, num2) { return num1 > num2 ? num1 : num2; }; console.log(maximum(10, 20)); // 20 console.log(max(10, 20)); // ReferenceError: max is not defined 上例中,函式實字所建立的 var gcd = function g(num1, num2) { return num2 != 0 ? g(num2, num1 % num2) : num1; }; console.log(gcd(10, 5)); // 5 在 EMCAScript 5 之前的版本,在一個匿名函式中,如果想取得本身實例,可以藉由 var gcd = function(num1, num2) { return num2 != 0 ? arguments.callee(num2, num1 % num2) : num1; }; console.log(gcd(10, 5)); // 5 不過在 ECMAScript 5 嚴格模式下,不能使用 'use strict' var gcd = function(num1, num2) { return num2 != 0 ? arguments.callee(num2, num1 % num2) : num1; }; console.log(gcd(10, 5)); // TypeError 函式既然是物件,本身亦可擁有特性。例如函式有個 var gcd = function g(num1, num2) { return num2 != 0 ? g(num2, num1 % num2) : num1; }; console.log(gcd.length); // 2 函式也可以擁有方法,這個之後再談,你也可以在其上新增特性或方法,就如同一個普通的物件。 函式宣告與函式實字在運用上,幾乎是相同的,但還是有細微的差別。例如,以下可以正常執行: func(); function func() { console.log('func'); } 不過以下會發生 func(); // TypeError: undefined is not a function var func = function() { console.log('func'); }; 錯誤訊息告訴你, 而在第二個程式中,僅宣告了 雖然不重要,但還是提一下的是,以上兩種方式,在遇到要建立函式實例時,都不會再重新直譯,但如果你以直接建構 var max = new Function('num1', 'num2', 'return num1 > num2 ? num1 : num2' ); |