前言
學(xué)習(xí)函數(shù)新增內(nèi)容扩借,需要先了解ES6的變量解構(gòu)賦值。
本文大量引用阮一峰老師的ES6手冊农曲。
為函數(shù)的參數(shù)設(shè)置默認(rèn)值
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
const p = new Point();
console.log(p);
注意事項:
- 函數(shù)內(nèi)部不允許給參數(shù)重復(fù)聲明秉馏,比如用var、let唆涝、const聲明找都。但可以重復(fù)賦值。
- 參數(shù)默認(rèn)值不是傳值的廊酣,而是每次都重新計算默認(rèn)值表達(dá)式的值能耻,即時從前計算過,也當(dāng)做沒計算過亡驰。也就是說晓猛,參數(shù)默認(rèn)值是惰性求值的。
參數(shù)默認(rèn)值跟解構(gòu)賦值配合使用
首先你要懂ES6變量解構(gòu)賦值凡辱。
下面是利用對象的解構(gòu)賦值戒职,函數(shù)聲明的參數(shù)模式,必須與傳入值的模式匹配:
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 傳入undefined洪燥,模式不匹配,所以報錯
如果想要什么參數(shù)都不傳乳乌,也依然不報錯捧韵,怎么做?依然是利用解構(gòu)賦值汉操,下面代碼中再来,{x, y = 5} = {}
表示如果整個參數(shù)不存在,就默認(rèn)為空對象磷瘤,然后再計算x和y各是多少芒篷,x因為沒有對應(yīng)值,當(dāng)然是undefined采缚,y雖然也沒有對應(yīng)值针炉,但是有默認(rèn)值,所以y是5仰担。
function foo({x, y = 5} = {}) {
console.log(x, y);
}
foo() // undefined 5
對比下面兩段代碼糊识,它們的結(jié)果是一樣的绩社。區(qū)別在哪摔蓝?
寫法一:
- 沒有傳參赂苗,所以用默認(rèn)參數(shù),也就是空對象贮尉。
- x和y先去空對象尋找對應(yīng)值拌滋,找不到,所以用自己的默認(rèn)值猜谚。
寫法二:
- 沒有傳參败砂,所以用默認(rèn)參數(shù)
{ x: 0, y: 0 }
。 - x和y去
{ x: 0, y: 0 }
尋找對應(yīng)值魏铅,找到了對應(yīng)值昌犹,所以直接用對應(yīng)值。
區(qū)別就是在哪一步設(shè)默認(rèn)览芳。所以斜姥,在任意一步設(shè)默認(rèn)都可以,只要是合法的js代碼沧竟。
// 寫法一
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
console.log(m1());
// 寫法二
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
console.log(m2());
參數(shù)默認(rèn)值應(yīng)該放到參數(shù)隊列最后面
這么做的目的是可以省略若干傳參铸敏。如果參數(shù)默認(rèn)值排在前面,沒有默認(rèn)值的參數(shù)反而在后面悟泵,那么傳參的寫法就會很不科學(xué)杈笔,比如:
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] 這種寫法雖然正確糕非,但是把undefined傳進(jìn)去代碼很丑
所以蒙具,如果參數(shù)有默認(rèn)值就應(yīng)該放到參數(shù)隊列最后面。
參數(shù)默認(rèn)值有特殊作用域
var x = 1;
// 2作為值傳給x朽肥,由于y = x是完全獨立的作用域禁筏,所以y的值是參數(shù)x的值,也是2
function f(x, y = x) {
console.log(y); // 打印2
}
f(2) // 2
let x = 1;
// 沒有傳入值鞠呈,所以y取默認(rèn)值融师,y=x形成獨立作用域,所以y是x的值蚁吝,x指向外層的1旱爆,也就是y是1。
function f(y = x) {
let x = 2; // 這個x不影響y的值
console.log(y); // 打印1
}
f() // 1
還有更復(fù)雜的情況窘茁,這里不多介紹了怀伦,更復(fù)雜的情況可能只會出現(xiàn)在面試題里,而實踐中山林,請讓自己的代碼條理清晰房待,這樣對自己,對別人,都有好處桑孩。
參數(shù)默認(rèn)值的一個應(yīng)用
先定義一個通用函數(shù)拜鹤,不干別的,只負(fù)責(zé)報錯:
function throwIfMissing() {
throw new Error('Missing parameter');
}
然后流椒,其他函數(shù)里面如果有不允許省略的參數(shù)敏簿,就賦值為這個函數(shù):
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(a, b, c = throwIfMissing()) {
}
foo(1,2); // Uncaught Error: Missing parameter
rest參數(shù)
學(xué)習(xí)變量解構(gòu)賦值的時候,我們就遇到了rest變量宣虾,也就是:
let [a, b, ...c] = [1,2,3,4,5,6,7];
console.log(c); // [3,4,5,6,7]
參數(shù)也有這種寫法惯裕,表示剩余的傳參,有多少我全包了绣硝。
function add(...values) {
let sum = 0;
for (var val of values) { // values 是一個數(shù)組蜻势,包含所有傳入的參數(shù)
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
所以,到現(xiàn)在鹉胖,傳參真的可以為所欲為握玛,根本不再是ES5時代的參數(shù)必須一對一:
- 你有一系列參數(shù),我用比如
...args
就可以打包次员。 - 如果你傳入一個數(shù)組败许,我用解構(gòu)賦值就可以打散。
箭頭函數(shù)
箭頭函數(shù)是ES6對函數(shù)寫法的最大修改淑蔚,改到人們一開始都不認(rèn)識市殷。
var f = v => x;
// 等價于
var f = function(v) {
return x;
};
代碼塊部分多于一條語句,就要使用大括號將它們括起來刹衫,并且使用return語句返回醋寝。
var sum = (num1, num2) => num1 + num2;
// 等價于
var sum = function(num1, num2) {
return num1 + num2;
};
由于大括號是被默認(rèn)解釋為代碼塊,所以如果箭頭函數(shù)直接返回一個對象带迟,必須在對象外面加上括號音羞,否則會報錯。
// 報錯
let getTempItem = id => { id: id, name: "Temp" };
// 不報錯
let getTempItem = id => ({ id: id, name: "Temp" });
箭頭函數(shù)可以與變量解構(gòu)結(jié)合使用:
const full = ({ first, last }) => first + ' ' + last;
// 等價于
function full({ first, last }) {
return first + ' ' + last;
}
// 使用函數(shù)的時候就full({first: 4, last: 8});就可以了
箭頭函數(shù)寫法的優(yōu)勢:
- 簡練仓犬。定義一個判斷是偶數(shù)的函數(shù)如下嗅绰,因為這個函數(shù)就是參數(shù)跟運算,所以箭頭函數(shù)很簡練:
const isEven = n => n % 2 == 0;
- 簡化回調(diào)函數(shù)搀继。ES5時代窘面,為了清洗的寫回調(diào)函數(shù),往往要折行寫代碼叽躯,現(xiàn)在就簡化了财边。
// 正常函數(shù)寫法
[1,2,3].map(function (x) {
return x * x;
});
// 箭頭函數(shù)寫法
[1,2,3].map(x => x * x);
// 正常函數(shù)寫法
var result = values.sort(function (a, b) {
return a - b;
});
// 箭頭函數(shù)寫法
var result = values.sort((a, b) => a - b);
注意,箭頭函數(shù)不是永遠(yuǎn)等價于常規(guī)寫法点骑。區(qū)別如下:
常規(guī)寫法中酣难,this對象的指向是可變的谍夭,但是在箭頭函數(shù)中,它是固定的憨募。這也是ES6為了降低js學(xué)習(xí)難度所做的改變紧索。常規(guī)寫法中,函數(shù)體內(nèi)的this對象馋嗜,不一定是定義時所在的對象齐板,而是使用時所在的對象吵瞻。但是葛菇,箭頭寫法中,函數(shù)體內(nèi)的this對象橡羞,就是定義時所在的對象眯停,而不是使用時所在的對象。
不可以當(dāng)作構(gòu)造函數(shù)卿泽,也就是說莺债,不可以使用new命令,否則會拋出一個錯誤签夭。
不可以使用arguments對象齐邦,該對象在函數(shù)體內(nèi)根本不存在。如果要用第租,可以用 rest 參數(shù)代替措拇。
不可以使用yield命令,因此箭頭函數(shù)不能用作 Generator 函數(shù)慎宾。