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,因爲暫時性死區的問題,會報錯。