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


Closure 閉包

  • function 與 lexical environment (作用域環境) 的組合
  • free variable
  • 函式捕捉的是變數,並非變數值。

      var fs = [];
      for (var i = 0; i < 10; ++i){
          fs[i] = function(){
              return i;   // 捕捉的是變數 i
          }
      }
    
      console.log(fs[0]());   // 10
      console.log(fs[1]());   // 10
    
    • 捕捉變數值解法1

        // ex1. ES6以前
        function foo(i){
            return function(){
                return i;
            }
        }
      
        var fs = [];
        for(var i = 0; i < 10; ++i){
            fs[i] = foo(i);   // var有function scope
        }
      
        console.log(fs[0]());   // 0
        console.log(fs[1]());   // 1
      
    • 捕捉變數值解法2

        // ex2. ES6 let
        let fs = [];
        for(let i = 0; i < 10; ++i){
            // let 每次迭代都是重新宣告一個同名變數 i
            fs[i] = function(){
                return i;
            }
        }
      
        console.log(fs[0]());   // 0
        console.log(fs[1]());   // 1
      



Generatror 產生器函式

  • * 產生器關鍵字
      function* generator(){
          // ...
      }
    
  • yield 關鍵字

    1. 只能在產生器函式中使用
    2. return 是直接停止函式運行
    3. yield 是暫時把控制權讓回給函式呼叫者,函式不會因此停止。
    4. yield 實際上會傳回產生器物件(generator object)
    5. 以下範例 for...of 每次迭代會呼叫 next() 方法,直到迭代出來的物件 donetrue

       function* range(start, end){
           for(let i = start; i < end; ++i){
               yield i;       // 控制權先回到 main program
           }
       }
      
       // main program
       let g = range(3, 10);
       for (let num of g){
           console.log(num);
       }
      
  • Generator object 產生器物件

      function* range(start, end){
          for(let i = start; i < end; ++i){
              yield i;
          }
      }
    
      let g = range(1, 3);
      g.next();   // {value: 1, done: false}
      g.next();   // {value: 2, done: false}
      g.next();   // {value: undefined, done: true}
    
    • .next() 方法
      1. type {value: any, done: boolean}
      2. e.g., {value: 3, done: false}
      3. 每次呼叫 .next() 才會運算並傳回下個產生值
      4. 可實現 惰性求值
      5. for..of每次迭代都會呼叫 .next(),所以產生器具有迭代器的行為 可用來當作 Iterator 以實作 Symbol.iterator
  • 產生器可以給呼叫者值,相反地,呼叫者也可以指定值給產生器

      function* producer(n){
          for(let data = 0; data < n; ++data){
              console.log('生產了:', data);
              yield data;
          }
      }
    
      function* consumer(n){
          for(let i = 0; i < n; ++i){
              let data = yield;
              console.log('消費了:', data);
          }
      }
    
      function clerk(n, producer, consumer){
          console.log('執行', n, '次生產與消費');
          let p = producer(n);
          let c = consumer(n);
          c.next();             // 先執行至 let data = yield;
          for(let data of p){
              c.next(data);
          }
      }
    
      clerk(5, producer, consumer);
    
    • return() 直接要求產生器停止迭代
    • throw()
  • Generator函式內也可以用 return

    • 以下範例若用 for.. of 迭代不會迭代出 return 傳回值

      function* range(start, end){
        for(let i = start; i < end; ++i){
            yield i;
        }
        return 'return';
      }
      
      let g = range(1, 3);
      g.next();   // {value: 1, done: false}
      g.next();   // {value: 2, done: false}
      g.next();   // {value: 'return', done: true}
      
  • 建立可直接取得其他產生器資料的產生器

      // ex1.
      function* range(start, end){
          for(let i = start; i < end; ++i){
              yield i;
          }
      }
    
      function* np_range(n){
          for(let i of range(0 - n, 0)){
              yield i;
          }
          for(let i of range(1, n + 1)){
              yield i;
          }
      }
    
      for(let i of np_range(3)){
          console.log(i);
      }
    
      // ex2. 使用 `yield*` 關鍵字改寫 ex1
      function* range(start, end){
          for(let i = start; i < end; ++i){
              yield i;
          }
      }
    
      function* np_range(n){
          yield* range(0 - n, 0);
          yield* range(1, n + 1);
      }
    
      for(let i of np_range(3)){
          console.log(i);
      }
    



(ES6) Template string & tagged template

  • Template string 模板字串
      // Template string 模板字串
      `Hello ${name} !`
    
  • Tagged template 標記模板

      // Tagged template 標記模板
      f(`${a} + ${b} = ${ a + b }`)   // f('10 + 20 = 30')
    
      f`${a} + ${b} = ${ a + b }`    // f(['', ' + ', ' = ', ''], 10, 20, 30)
    
    • 標記模板應用

      1. String.raw 搭配tagged template可以忽略逃脫文字
         console.log(String.raw`ABC\nEFG`);   // ABC\nEFG
        
      2. 取得字串原始樣貌

         function f(strings){
             console.log(strings);
             console.log(strings.raw);
         }
        
         f`ABC\nEFG`   // ['`ABC\nEFG`'], ['`ABC\\nEFG`']
        
#javascript #book #note






你可能感興趣的文章

C# 各種類型轉換

C# 各種類型轉換

[week15]你真的懂了嗎?-自我檢測

[week15]你真的懂了嗎?-自我檢測

JavaScript 學習筆記 - Operator

JavaScript 學習筆記 - Operator






留言討論