ECMA Script 6_函數的擴展

ES6規定只要函數參數使用了默認值解構賦值、或者擴展運算符編程

那麼函數內部就不能顯式設定嚴格模式,不然會報錯數組

1. 參數的默認值app

ES6 容許爲函數的參數設置默認值,即直接寫在參數定義的後面函數式編程

函數不能有同名參數函數

  • 參數初始化會造成一個單獨做用域。實際執行的是 let a = 1;
  • 參數默認值是惰性求值的

每次調用函數foo,都會從新計算x + 1,而不是默認p等於 100優化

  • let x = 99; function foo(p = x + 1) { console.log(p); } foo(); // 100
     x = 100; foo(); // 101
  • function log(x, y = 'World') { console.log(x, y); } log('Hello');    // Hello World
    log('Hello', 'China');    // Hello China
    log('Hello', '');    // Hello
  • function Point(x = 0, y = 0) { this.x = x; this.y = y; } const p = new Point(); console.log(p); // { x: 0, y: 0 }

 

2. 函數的 length 屬性

返回沒有指定默認值的參數個數this

3. 參數初始化做用域spa

一旦設置了參數的默認值,函數進行參數聲明初始化時,參數會造成一個單獨的做用域(context)。prototype

等到初始化結束,這個做用域就會消失。rest

這種語法行爲,在不設置參數默認值時,是不會出現的。

參數初始化,實際上,執行的是 let 關鍵字聲明

4. rest 多餘 實參數組

...args                x, ...args

...數組名            實參, 多餘參數數組

實參列表,是一個真數組

用於獲取函數的多餘參數

  • 數組的操做
  • 函數中的操做
  • 利用 rest 參數改寫數組 push 方法的例子
  • function push(arr, ...items) { items.forEach(function(item) { arr.push(item); console.log(item); }); }; var arr = []; push(arr, 1, 2, 3)
  • 函數的 length 屬性,不包括 rest 參數,由於 rest 參數表示 多餘的實參列表
  • console.log((function(a) {}).length);    // 1
    console.log((function(...a) {}).length);    // 0
    console.log((function(a, ...b) {}).length);    // 1

 

5. 箭頭函數 const func = () => {}; 

簡化原來的 const func = function(){};

箭頭函數 沒有本身的顯式原型屬性,即 func.prototype = undefined;

箭頭函數 不能做爲構造函數使用,即不能 new 調用

箭頭函數 的 this 指向最近的包裹函數的 this 一致,若是沒有函數包裹,則 this 指向 window

箭頭函數 可讓 this 指向固定化,這種特性頗有利於封裝回調函數

實際緣由是箭頭函數根本沒有本身的 this,致使內部的 this 就是外層代碼塊的 this。

正是由於它沒有 this,因此也就不能用做構造函數

於箭頭函數沒有本身的 this,因此固然也就不能用call()、apply()、bind()這些方法去改變this的指向

其實箭頭函數也沒有 argumentssupernew.target

箭頭函數的函數體內 不能夠使用 arguments 對象,該對象在函數體內不存在。若是要用,能夠用 rest 參數代替

不能夠使用 yield 命令,所以箭頭函數不能用做 Generator 函數

  • var handler = { id: '123456', init: function() { document.addEventListener('click', event => this.doSomething(event.type), false); }, doSomething: function(type) { console.log('Handling ' + type  + ' for ' + this.id); } };
  • 形參 只有一個時,能夠省略括號

const func = (x) => {};

const func = x => {};

  • 函數體 只有一條語句時,能夠省略花括號,並 return 這條語句的結果;

const func = x => { x += 1;};

const func = x => x+=1;

  • 因爲花括號被解釋爲代碼塊,因此若是 箭頭函數 想要直接返回一個對象,必須在對象外面加上括號,不然會報錯
  • let getTempItem = id => ({ id: id, name: "Temp" });
  • 簡化了 回調函數
  • // 正常函數寫法
    [1,2,3].map(function (x) { return x * x; }); // 箭頭函數寫法
    [1,2,3].map(x => x * x);

rest 參數 與 箭頭函數的結合使用

  • const numbers = (...nums) => nums; numbers(1, 2, 3, 4, 5);    // [1,2,3,4,5]
     const headAndTail = (head, ...tail) => [head, tail]; headAndTail(1, 2, 3, 4, 5);    // [1,[2,3,4,5]]
  • 如下是錯誤代碼,不適用場合:

定義函數的方法,且該方法內部包括 this

  • const cat = { lives: 9, jumps: () => { this.lives--; // this 指向 window ,因此結果確定是有問題的 }; };

須要動態this的時候,也不該使用箭頭函數

  • var button = document.getElementById('press'); button.addEventListener('click', () => { this.classList.toggle('on'); });

6. 雙冒號運算符——函數綁定運算符——"對象::函數(參數列表)"

背景:

箭頭函數能夠綁定 this 對象,大大減小了顯式綁定 this 對象的寫法(call、apply、bind)。

可是,箭頭函數並不適用於全部場合,因此如今有一個提案,提出「函數綁定」(function bind)運算符,

用來取代 call、apply、bind 調用箭頭函數能夠綁定 this 對象,大大減小了顯式綁定 this 對象的寫法(call、apply、bind)

  • foo::bar; // 等同於
    bar.bind(foo); foo::bar(...arguments); // 等同於
    bar.apply(foo, arguments); const hasOwnProperty = Object.prototype.hasOwnProperty; function hasOwn(obj, key) { return obj::hasOwnProperty(key); };

7. 尾調用 優化

函數調用 會在內存造成一個「調用記錄」,又稱「調用幀」(call frame),保存調用位置內部變量等信息。

若是在函數 A 的內部調用函數 B ,那麼在 A 的調用幀上方,還會造成一個 B 的調用幀。

等到 B 運行結束,將結果返回到 A,B 的調用幀纔會消失。若是函數 B 內部還調用函數 C,

那就還有一個 C 的調用幀,以此類推。全部的調用幀,就造成一個「調用棧」(call stack)

 

  • 尾調用(Tail Call)

指某個函數的最後一步操做調用另外一個函數

是函數式編程的一個重要概念

只在嚴格模式下開啓,正常模式是無效的

  • function f(x){ return g(x); };
  • 如下不屬於尾調用
  • // 狀況一
    function f(x){ let y = g(x); return y; }; // 狀況二
    function f(x){ return g(x) + 1; }; // 狀況三
    function f(x){ g(x); };
  • 尾調用優化」(Tail call optimization),

即只保留內層函數的調用幀。若是全部函數都是尾調用,那麼徹底能夠作到每次執行時,調用幀只有一項,這將大大節省內存。

內層函數 若是用到了 外層函數 的變量

8. 尾遞歸

若是尾調用自身,就稱爲尾遞歸

遞歸很是耗費內存,由於須要同時保存成千上百個調用幀,很容易發生「棧溢出」錯誤(stack overflow)

尾遞歸的實現: 每每須要改寫 遞歸函數,確保最後一步只調用自身。

把全部用到的內部變量改寫成函數的參數。

採用 ES6 的函數默認值

函數式編程有一個概念,叫作柯里化(currying),意思是 將多參數的函數轉換成單參數的形式。

  • 將 遞歸函數 改寫成 尾遞歸,只保留一個調用記錄,複雜度 O(1) 
  • function factorial(n, total) { if (n === 1){ return total; }; return factorial(n - 1, n * total); }; factorial(5, 1);     // 120
  • 優化過的 斐波拉契 函數  
  • function Fibonacci2 (n , ac1 = 1 , ac2 = 1) { if( n <= 1 ){ return ac2; }; return Fibonacci2 (n - 1, ac2, ac1 + ac2); } Fibonacci2(100);    // 573147844013817200000
    Fibonacci2(1000);    // 7.0330367711422765e+208
    Fibonacci2(10000);    // Infinity

9. 函數參數的尾逗號

 

容許定義和調用時,尾部直接有一個逗號

函數參數與數組和對象的尾逗號規則,保持一致了

  • function clownsEverywhere(param1, param2, ) { /* ... */ } clownsEverywhere('foo', 'bar', );
相關文章
相關標籤/搜索