函數拓展

es6對函數做了很友好的修改,使得函數的編寫更加的方便。

1、函數默認值問題

在es5中我們要給一個函數參數設置默認值,一般都是使用||來完成。

function log(x, y) {
  y = y || 'World';
  console.log(x, y);
}

這樣我們在調用的時候如果只傳遞參數x也可以打印出world,y參數被賦予了默認值。es6中,在函數定義的時候,允許在參數中直接賦予默認值。

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

這樣實現的效果是一樣的,這樣的語法解構,使得代碼有了更強的可讀性。後續開發和維護的人員可以很容易的看到哪些參數是有默認值的,而且也知道默認值是什麼。

函數的參數是默認聲明的,所以在函數體內不能使用let或者const來再次聲明變量。

function foo(x = 5) {
  let x = 1; // error
  const x = 2; // error
  var x = 3; //right
}

而且在使用默認值的時候,參數是不可以重複的。

// 不報錯
function foo(x, x, y) {
  // ...
}

// 報錯
function foo(x, x, y = 1) {
  // ...
}

其實我們可以理解爲在es6的默認值函數中,變量是通過let或者const聲明過的,所以當我們再次聲明的時候就會報錯。

參數默認值並不是單純的傳遞值,其實是每次調用都會重新計算默認值的值,也就是所謂的惰性求值。默認值可以設爲一個表達式

let x = 99;
function foo(p = x + 1) {
  console.log(p);
}

foo() // 100
x = 101;
foo() // 102

上面的例子中調用p默認值的時候是先計算x+1,然後再傳遞給p,也只有在調用的時候纔會執行x+1。你會發現默認值既然是一個計算過程,如果傳遞的是個對象,然後對改對象做相應的映射處理,是不是也可以?沒錯,這就是解構賦值相結合的默認值問題。我們傳遞對象的時候,在默認值的表達式中完成解構賦值,是完全可以的。

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

foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined

上面的例子中,傳遞對象的時候,函數對傳遞的參數進行了解構,傳遞對象中的y默認值設爲5,但是並沒有對參數設默認值,所以單純的使用函數foo()還是會報錯。如果我們提供參數的默認值

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

這樣就算不傳默認對象,也不會出錯。

對於默認值的聲明,一般在函數的定義中,有默認值的參數要寫在函數的尾參數,如果默認值的位置不是尾參數,在調用函數的時候,如果想傳遞後面的參數,那麼這個有默認值的中間參數是不可以省略的。

function f(x, y = 5, z) {
  return [x, y, z];
}

f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 報錯
f(1, undefined, 2) // [1, 5, 2]

2、函數長度問題

函數的length正常情況下是會返回參數個數,但是在使用默認值後的函數length就只會返回沒有默認值的參數個數,也就是說有默認值的函數length失真了。而且如果默認值不是尾函數,默認值後面的參數也不算入函數長度內。

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
(function(...args) {}).length // 0
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1

3、函數的作用域問題

一旦設置了參數的默認值,函數進行聲明初始化時,參數會形成一個單獨的作用域。等到初始化結束,這個作用域就會消失。這種語法行爲,在不設置參數默認值時,是不會出現的。

var x = 1;

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

f(2) // 2

在調用函數的時候y=x中x是第一個參數x,

let x = 1;

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

f() // 1

上面的例子中,y=x,x在函數作用域內是不存在的,所以指向外層x=1。但是如果像上面的例子那樣,如果x不存在,就會報錯。

var x = 1;

function foo(x = x) {
  // ...
}

foo() // ReferenceError: x is not defined

上面的例子中,因爲單獨作用於的問題,let x=x,因爲暫時性死區的問題,會報錯。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章