ES6入門之函數的擴展

1. 函數參數的默認值


1.1 用法

在ES6以前是不能爲函數的參數指定默認值的,要想實現默認值只能經過判斷賦值的方式來實現,在ES6中容許函數爲參數設置默認值,主要是爲了提升代碼的可閱讀性,有利於代碼的優化。另外注意的是在參數賦值的時候,該參數不能重複使用,不能使用let const 進行定義。前端

// ES6 以前實現
function log(x, y) {
  y = y || 'World';
  if (typeof y === 'undefined') {
      y = 'World';
  }
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World


// ES6 中實現
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();
p // { x: 0, y: 0 }

function foo(x = 5,x) { 
  let x = 1; // 報錯,不能同名參數,不能對參數進行let const 定義
  const x = 2;
}
複製代碼
1.2 與解構賦值一塊兒使用

若是函數在調用的時候沒有提供參數,內部變量就不會產生,就會產生錯誤,經過提供函數的默認值能夠解決這種問題,以下:編程

function foo({x, y = 5}) {
  console.log(x, y);
}
foo() // 報錯
foo({x:1}) // 1 5
foo({x:2,y:3) // 2 3
foo({}) // undefined 5

function foo ({x,y = 5} = {}){
    console.log(x,y)
}
foo() // undefined 5 
這樣就是若是沒有在調用的時候傳值 就默認賦空對象。
複製代碼

以下例子:segmentfault

function post(url, {b = '',type='get',h={}}){
    console.log(type)
}

post('w.b.c',{}) // get
post('w.b.c')    // 報錯

// 改爲這樣就能夠了

function post(url, {b = '',type='get',h={}} = {}){
    console.log(type)
}

post('w.b.c',{}) // get
post('w.b.c')    // get
複製代碼

下面例子的區別數組

// 寫法一
function m1({x = 0, y = 0} = {}) {
  return [x, y];
}

// 寫法二
function m2({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

兩個都是有默認值在調用的時候都傳值或者都不傳值的時候狀況是同樣的。
可是若是傳空值,或者不傳值的狀況會有差別以下:

m1({}) // 由於自己有默認值 因此爲 [0,0]
m2({}) // 默認值爲空 解構賦值沒有傳值 因此 [undefined,undefined]

// 其餘狀況同上
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
複製代碼
1.3 參數默認值的位置

若是定義了默認值的參數,應該是函數的尾參數。並且這個參數是沒法省略的,除非輸入undefinedapp

1.4 函數的 length 屬性

函數參數指定了默認值以後,函數的length屬性將會減去指定了默認值的參數個數。由於該屬性認爲,指定了默認值的參數將不包含在預期參數個數中。以下:ide

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
複製代碼
1.5 做用域

若是函數中的參數設置了默認值,那麼函數在聲明初始化的時候,參數會造成一個單獨的做用域,初始化完成後這個做用域就會消失,這種狀況只在參數設置了默認值的狀況下。以下:函數式編程

var x = 1;

function f(x, y = x) {
  console.log(y);
}

f(2) // 2
// 由於 設置了默認值 因此在調用 f 的時候就造成了做用域,這時候由於將x賦值給y 
傳入的x 爲 2  因此y是2,若是這時候 調用的時候不傳值,
那麼x將指向全局,因此y = 1
複製代碼
1.6 應用

利用參數默認值,能夠指定某一個參數不得省略,若是省略就報錯,以下函數

function throwIfMissing() {
  throw new Error('Missing parameter');
}

function foo(mustBeProvided = throwIfMissing()) {
  return mustBeProvided;
}

foo()
// Error: Missing parameter
foo(2) // 2
複製代碼

2. rest 參數

ES6 中 增長了 rest 參數(...變量名),用於獲取函數多餘的參數,rest參數搭配的變量是一個數組,該變量將多餘的參數放入數組中。post

function add(...values) {
  let sum = 0;

  for (var val of values) {
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10

// 注意:rest 參數以後不能再有其餘參數,另外rest參數也不計算在
函數的length屬性中。
複製代碼

3. 嚴格模式

ES6 中,若是函數參數使用了默認值,解構賦值,或者擴展運算符,那麼函數內部將不能顯式設定爲嚴格模式,不然會報錯。由於函數執行的時候 先執行函數參數,在執行函數體,可是由於只有在函數體中才能知道參數是否以嚴格模式執行,可是參數卻應該先於函數執行。有兩種方法能夠規避:1、 設置全局嚴格模式,2、把函數包在一個無參數的當即執行函數裏面。優化

4. name屬性

返回函數的函數名,以下:

function foo(){}
foo.name // foo

var f = function(){}
// ES5
f.name // ''
// ES6
f.name // f

var f = function c(){}

f.name // c
複製代碼

5. 箭頭函數

ES6 容許使用 「箭頭」 (=>)定義函數

var f = v => v;

// 等同於
var f = function (v) {
  return v;
};

var f = () => 5;
// 等同於
var f = function () { return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同於
var sum = function(num1, num2) {
  return num1 + num2;
};

// 若是箭頭函數後面的語句較多就要用大括號包裹起來 並return返回
var sum = (num1, num2) => { return num1 + num2; 

//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]]
複製代碼
注意點
1. 函數體內的this對象,就是在定義時所在的對象,而不是使用時所在的對象。
2. 不能夠看成構造函數,也就是說,不可使用new命令,不然會拋出一個錯誤。
3. 不可使用arguments對象,該對象在函數體內不存在。若是要用,能夠用 rest 參數代替。
4. 不可使用yield命令,所以箭頭函數不能用做 Generator 函數。
5. 因爲箭頭函數沒有本身的this,因此固然也就不能用call()、apply()、bind()這些方法去改變this的指向。
複製代碼
不適用場景
1. 定義對象的方法,且該方法內部包括this
2. 動態定義this 的場合,如點擊事件中this 的指向
複製代碼
嵌套的箭頭函數

箭頭函數內部能夠在嵌套使用箭頭函數。

6. 尾調用優化

什麼是尾調用

函數式編程的一個重要概念,指某個函數的最後一步是調用另外一個函數

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);
}
複製代碼
尾調用優化

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

function f() {
  let m = 1;
  let n = 2;
  return g(m + n);
}
f();

// 等同於
function f() {
  return g(3);
}
f();

// 等同於
g(3);
複製代碼

注意,只有再也不用到外層函數的內部變量,內層函數的調用幀纔會取代外層函數的調用幀,不然就沒法進行「尾調用優化」。

尾遞歸

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

ES6 的尾調用優化只在嚴格模式下開啓,正常模式是無效的。
尾遞歸優化的實現

在正常模式下,可使用減小調用棧,採用循環換掉遞歸的方法

歡迎關注 公衆號【小夭同窗】

歡迎關注 公衆號【小夭同窗】

ES6入門系列

ES6入門之let、cont

ES6入門之變量的解構賦值

ES6入門之字符串的擴展

ES6入門之正則的擴展

ES6入門之數值的擴展

Git教程

前端Git基礎教程

相關文章
相關標籤/搜索