讀書筆記: JavaScript技術手冊3 - 函式基本語法

函式這部分整理很多容易忽略的重要語法細節,可以常看。


基本語法

函式表示某個重用流程的封裝(encapsulation)

函式定義與呼叫

  • 函式定義
    • function declaration 函式宣告
      • JS engine會優先處理函式宣告
    • function literal 函式實字
      • 一種expresstion
      • 執行運算式之後才產生function object
      • 可附加/不附加函式實字的函式名稱
        • Anonymous function 匿名函式:不附加函式名稱
        • 附加函式名稱通常是為了遞迴
    • Arrow function 箭頭函式
  • IIFE(immediately invoked function expression)
    • (function(){...})();
    • 外層兩個括號的前面一個 () 只是為了區分function和後面的() operator,例如:(45).toExponential()
  • JS不支援函式重載(overload)(可設定參數預設值避免)

      function sum(a, b){
          reutrn a + b;
      }
    
      function sum(a, b, c){    // 覆蓋前一個 sum()
          reutrn a + b + c;
      }
    
      console.log(sum(1, 2));   // NaN, ∵ c 是 undefined
    
  • return

    1. 預設回傳 undefined
      • 所以沒有 returnreturn 不指定傳回值就會回傳 undefined
    2. 陳述句可不用 ; 結束判定,但可能造成某些問題:

       // bad.js
       function foo(){
           return            // 會在這行直接離開函式並跳回函式呼叫處
           {
               x: 10
           }
       }
       console.log(foo());   // undefined
      
       // good.js
       function foo(){
           return {
               x:10
           };
       }
      
    3. 所以在 JS 千萬不要跟以下範例 bad.js 相同讓 {} 在同一縮排(以免以為 換行 代表結束)

Fitst-class function 一級函式

  • 每個函式都是物件 && Function的instance
  • → 可當作 "值" 指定給其他變數
    • callback function

Local function 區域函式

  • 定義:函式內部宣告的函式
  • 優點:可 直接存取外部函式的參數先前宣告的區域變數,以減少函數呼叫時要傳遞的引數。

      // selectSort.js
      function sort(nums){
          // local functiob
          function minIndex(left, right){
             if (right === nums.length){
                 return left;
             } else if (nums[right] < nums[left]){
                 return minIndex(rigth, right + 1);
             } else{
                 return minIndex(left, right + 1);
             }
          }
    
          // main function
          for(let i = 0; i < nums.length; ++i){
              let selected = minIndex(i, i + 1);
              if (i !== selected){
                  // swap i with selected
                  let temp = nums[i];
                  nums[i] = nums[selected];
                  nums[selected] = temp;
              }
          }
      }
    

Callback function 回呼函式

  • 定義:被傳入函式中的函式

      // filter1.js
      function filter(arr, predicate){
          let result = [];
          for (let elem of arr){
              if (predicate(elem)){
                  result.push(elem);
              }
          }
          return result;
      }
    
      // test
      function lengGreaterThan5(elem){
          return elem.length > 5;
      }
    
      function lengLessThan3(elem){
          return elem.length < 3;
      }
    
      function (elem){
          return elem.includes('a');
      }
    
      let fruits = ['apple', 'banana', 'cherry'];
      console.log(filter(arr, lengGreaterThan6));   // ['banana', 'cherry']
      console.log(filter(arr, lengLessThen3));      // []
      console.log(filter(arr, hasa));               // ['apple', 'banana']
    
  • 結合local function 和 callback function 範例

      // filter2.js
      function filter(arr, predicate){
          let result = [];
          for (let elem of arr){
              if (predicate(elem)){
                  result.push(elem);
              }
          }
          return result;
      }
    
      function lengGreaterThan(num){
          function lengGreaterThanNum(elem){
              return elem.length > num;
          }
          return lengGreaterThanNum;
      }
    
      let fruits = ['apple', 'banana', 'cherry'];
      console.log(filter(arr, lengGreaterThan(6)));   // ['banana', 'cherry']
      console.log(filter(arr, lengGreaterThan(10)));   // []
    

參數預設值

// ex1. ES6以前
function deposit(name, account, amount){
    return {
        name: name,
        account: account,
        amount: amount || 1000
    };
}

// ex2. ES6
function deposit(name, account, amount=1000){
    return {name, account, amount};   // ES6 支援的寫法
}

不定長度引數(ES6)

  • JS語法允許引數個數比參數多

    // ex1. ES6以前: 利用 arguments property
    function sum(){
        var sum = 0;
        for (var i = 0; i < arguments; ++i){
            sum += arguments[i];
        }
        return arguments;
    }
    
    // ex2. ES6
    function sum(...numbers){
        let total = 0;
        for (let number of numbers){
            total += number;
        }
        return total;
    }
    
  • Function object's properties
    • name
    • length 回傳參數個數(不包含預設參數與不定長度參數)



遞迴

  • 最大公因數範例

      // ex1. function declaration
      function gcd(m, n){
          if (n === 0){
              return m;
          }
          return gcd(n, m % n);
      }
    
      var r1 = gcd(8, 4);
      if (r1 === 1){
          console.log('互質');
      } else{
          console.log('最大公因數= ' + r);
      }
    
      // ex2. function literal
      let gcd = function g(n1, n2){
          return n2 !== 0 ? g(n2, n1 % n2) : n1;
      }
    
      // ex3. IIFE
      (function g(n1, n2){
          return n2 !== 0 ? g(n2, n1 % n2) : n1;
      })(8, 4);
    



Trick: Option object

  • 避免參數太多要一直反覆修正、還容易出現bug的技巧

      function ajax(url, option){
          let passedOption = {
              method: option.method || 'GET',
              contents: option.contents || '',
              dataType: option.dataType || 'text/plain',
              // ...以後要新增的參數可陸續加入作為 option object's property
          };
    
          // option 處理或其他處理
      }
    
      ajax('www.google.com.tw', {
          method: 'POST',
          contents: 'Get JavaScript'
      });
    



JS built-in function

  • ECMAScropt .sort()

    • 沒有規範JS內建的 .sort() 結果一定是穩定(stable)排序

        function ascend(n1, n2){
            return n1[0] - n2[0];
        }
      
        function descend(n1, n2){
            return n2[0] - n1[0];
        }
      
        let arr = [[4, 1], [3, 1], [3, 7], [10, 6]];
        console.log(arr.sort(ascend));
        console.log(arr.sort(descend));
      
        // ascend 結果
        [[3, 1], [3, 7], [4, 1], [10, 6]]  // 穩定排序
        [[3, 7], [3, 1], [4, 1], [10, 6]]  // 不穩定排序
      
    • 預設排序方式是根據Unicode碼點,若傳入元素不是字串就會先轉為字串再排序



References