前言
本章介紹函數(shù)的擴(kuò)展。有些不常用的知識(shí)了解即可疯汁。
本章原文鏈接:函數(shù)的擴(kuò)展寥院。
函數(shù)參數(shù)的默認(rèn)值
ES6 允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值,即直接寫在參數(shù)定義的后面涛目。
當(dāng)函數(shù)形參沒(méi)有被賦值時(shí)秸谢,才會(huì)將默認(rèn)值賦值給函數(shù)參數(shù)。
// 默認(rèn)值直接寫在行參后面
function sampleFn(sample = 0, sample1 = 0) {
return sample + sample1;
}
注意:
- 參數(shù)變量是默認(rèn)聲明的霹肝,所以不能用
let
或const
再次聲明估蹄。 - 使用參數(shù)默認(rèn)值時(shí),函數(shù)不能有同名參數(shù)沫换。
- 參數(shù)默認(rèn)值是惰性求值的臭蚁。
- 函數(shù)的默認(rèn)值指定后,函數(shù)
length
屬性返回的是沒(méi)有指定默認(rèn)值的參數(shù)的個(gè)數(shù)讯赏。 - 參數(shù)的默認(rèn)值一旦設(shè)定垮兑,函數(shù)進(jìn)行聲明初始化時(shí),參數(shù)會(huì)形成一個(gè)單獨(dú)的作用域(context)漱挎。
// 默認(rèn)值直接寫在行參后面
function sampleFn(sample = 0, sample1 = 0,sample = 1) { // 不能有同名參數(shù)
let sample = 1; // 不能再次聲明
return sample + sample1;
}
注意:通常情況下系枪,定義了默認(rèn)值的參數(shù),應(yīng)該是函數(shù)的尾參數(shù)磕谅。也就是放在最后面私爷。
解構(gòu)賦值默認(rèn)值
// 函數(shù)的默認(rèn)值與結(jié)構(gòu)賦值的默認(rèn)值可以結(jié)合使用
function sampleFn({ sample = 0, sample1 = 0 } = {}) { // 函數(shù)參數(shù)默認(rèn)值
return sample + sample1;
}
console.log(sampleFn({ sample: 23, sample1: 33 })); // 56 參數(shù)需對(duì)應(yīng)解構(gòu)賦值的類型
作用域
當(dāng)函數(shù)參數(shù)設(shè)置了默認(rèn)值,函數(shù)進(jìn)行聲明初始化時(shí)膊夹,函數(shù)參數(shù)會(huì)生成一個(gè)單獨(dú)的作用域衬浑,等到初始化結(jié)束,該作用域就會(huì)消失放刨。而且該行為只在函數(shù)參數(shù)指定了默認(rèn)值才會(huì)出現(xiàn)工秩。
let sample = 1;
/*
在聲明的時(shí)候出現(xiàn)單獨(dú)作用域
在這個(gè)作用域中,變量沒(méi)有定義,于是指向外層變量
函數(shù)調(diào)用時(shí)助币,函數(shù)內(nèi)部變量影響不到默認(rèn)值變量
*/
function sampleFn(sample1 = sample) {
let sample = 2;
console.log(sample1);
return sample1;
}
sampleFn() // 1
rest 參數(shù)
ES6 引入 **rest 參數(shù) **浪听,用于獲取函數(shù)的多余參數(shù)。
arguments 對(duì)象
是類數(shù)組奠支,rest 參數(shù)
是真正的數(shù)組馋辈。
形式為:...變量名
抚芦,函數(shù)的最后一個(gè)命名參數(shù)以...
為前綴倍谜。
// 下面例子中 ...values 為 rest參數(shù) ,用于獲取多余參數(shù)
const sample = function (title, ...values) {
let sample = values.filter(
(item) => {
return item % 2 === 0;
}
)
return (title + sample);
}
console.log(sample("求偶數(shù)", 1, 2, 6, 2, 1)); // 求偶數(shù) 2, 6, 2
注意:rest參數(shù) 只能是函數(shù)的最后一個(gè)參數(shù)叉抡,函數(shù)的length不包括rest參數(shù)
嚴(yán)格模式
在JavaScript中尔崔,只要在函數(shù)中的嚴(yán)格模式,會(huì)作用于函數(shù)參數(shù)和函數(shù)體褥民。
ES2016 規(guī)定只要函數(shù)參數(shù)使用了默認(rèn)值季春、解構(gòu)賦值、或者擴(kuò)展運(yùn)算符消返,那么函數(shù)內(nèi)部就不能顯式設(shè)定為嚴(yán)格模式载弄,否則會(huì)報(bào)錯(cuò)。
function sample() { // 參數(shù)使用默認(rèn)值撵颊、解構(gòu)賦值宇攻、擴(kuò)展運(yùn)算符
'use strict'; // 開啟嚴(yán)格模式
}
name 屬性
函數(shù)的name屬性
,返回該函數(shù)的函數(shù)名倡勇。
function sample() { };
let sample1 = function () { };
function sample2() {};
console.log(sample.name); // sample
console.log(sample1.name); // sample1
// bound sample2 使用了 bind 方法逞刷,輸出會(huì)有bound前綴
console.log(sample2.bind({}).name);
console.log((new Function).name); // anonymous 構(gòu)造函數(shù)的name值為 anonymous
箭頭函數(shù)
簡(jiǎn)單介紹
ES 6 新增一種函數(shù)定義方法,使用箭頭連接參數(shù)列與函數(shù)題妻熊。
箭頭函數(shù)相當(dāng)于匿名函數(shù)夸浅,并且簡(jiǎn)化了函數(shù)定義,箭頭函數(shù)沒(méi)有prototype
扔役。
// 普通函數(shù)
let sample = function (item) {
return item;
};
// 上面函數(shù)等同于下面函數(shù)
// 使用箭頭函數(shù)
let sample = (item) => { return item}; // 箭頭函數(shù)
箭頭函數(shù)簡(jiǎn)寫
沒(méi)錯(cuò)帆喇,箭頭函數(shù)還可以簡(jiǎn)寫
- 當(dāng)參數(shù)只有一個(gè)時(shí),可以省略箭頭左邊的括號(hào)亿胸,但沒(méi)有參數(shù)時(shí)番枚,括號(hào)不可以省略。
- 當(dāng)函數(shù)體只有一個(gè)表達(dá)式時(shí)损敷,可省略箭頭右邊的大括號(hào)葫笼,但同時(shí)必須省略
return
語(yǔ)句 并寫在一行。 - 當(dāng)函數(shù)體分多于一條語(yǔ)句拗馒,就要使用大括號(hào)將它們括起來(lái)路星,并且使用
return
語(yǔ)句返回。
// 下面幾種函數(shù)寫法都相同
let sample = function (item) {
return item;
};
let sample = (item) => { return item}; // 箭頭函數(shù) 不省略
let sample = item => { return item}; // 省略左邊圓括號(hào)
let sample = (item) => item; // 省略右邊大括號(hào)和 return
let sample = item => item; // ?省略左邊圓括號(hào)和右邊花括號(hào)和return
// 如果不需要返回值的特殊情況
let sample = item => void item;
console.log(sample()); // undefined
注意點(diǎn)
- 箭頭函數(shù) 的
This
默認(rèn)指向定義它的作用域的This
。 - 箭頭函數(shù) 不能用作構(gòu)造函數(shù)洋丐。
- 箭頭函數(shù) 不可以使用
arguments
對(duì)象呈昔,該對(duì)象在函數(shù)體內(nèi)不存在。 - 箭頭函數(shù) 不可以使用
yield
命令友绝,也就不能作為 Generator 函數(shù)堤尾。
箭頭函數(shù)的this
箭頭函數(shù) 會(huì)繼承自己定義時(shí)所處的作用域鏈上一層的this
。
箭頭函數(shù) this
在定義的時(shí)候已經(jīng)確定了迁客,所以箭頭函數(shù)this
不會(huì)改變郭宝。
使用 call()
或 apply()
方法時(shí),也不能重新給箭頭函數(shù)綁定this
掷漱,bing()
方法無(wú)效粘室。
window.sample = "window 內(nèi) ";
function sampleFn() {
let thiz = this;
let sample = "sampleFn 內(nèi) ";
let sampleObj = {
sample: "sampleObj 內(nèi) ",
// 普通函數(shù)
sampleFn1: function () {
console.log(thiz === this);
console.log(this.sample);
},
// 箭頭函數(shù)
sampleFn2: () => {
// 箭頭函數(shù)的作用域?yàn)?sampleObj 上一層為 sampleFn
console.log(thiz === this); //箭頭函數(shù)的 this
console.log(this.sample);
}
}
sampleObj.sampleFn1(); // false, sampleObj 內(nèi)
sampleObj.sampleFn2(); // true, window 內(nèi)
}
sampleFn();
尾調(diào)用優(yōu)化
有兩個(gè)概念
尾調(diào)用
尾調(diào)用(Tail Call)是函數(shù)式編程的一個(gè)重要概念,就是指某個(gè)函數(shù)的最后一步是調(diào)用另一個(gè)函數(shù)卜范。尾遞歸
函數(shù)調(diào)用自身衔统,稱為遞歸。如果尾調(diào)用自身海雪,就稱為尾遞歸锦爵。
ES6 明確規(guī)定,所有 ECMAScript 的實(shí)現(xiàn)奥裸,都必須部署“尾調(diào)用優(yōu)化”险掀。
這就是說(shuō),ES6 中只要使用尾遞歸刺彩,就不會(huì)發(fā)生棧溢出(或者層層遞歸造成的超時(shí))迷郑,相對(duì)節(jié)省內(nèi)存。
這是什么意思呢创倔?
尾調(diào)用的作用嗡害,在原文中是這樣寫的:
我們知道,函數(shù)調(diào)用會(huì)在內(nèi)存形成一個(gè)“調(diào)用記錄”畦攘,又稱“調(diào)用幀”(call frame)霸妹,保存調(diào)用位置和內(nèi)部變量等信息。如果在函數(shù)A的內(nèi)部調(diào)用函數(shù)B知押,那么在A的調(diào)用幀上方叹螟,還會(huì)形成一個(gè)B的調(diào)用幀。等到B運(yùn)行結(jié)束台盯,將結(jié)果返回到A罢绽,B的調(diào)用幀才會(huì)消失。如果函數(shù)B內(nèi)部還調(diào)用函數(shù)C静盅,那就還有一個(gè)C的調(diào)用幀良价,以此類推。所有的調(diào)用幀,就形成一個(gè)“調(diào)用椕鞴福”(call stack)蚣常。
尾調(diào)用由于是函數(shù)的最后一步操作,所以不需要保留外層函數(shù)的調(diào)用幀痊银,因?yàn)檎{(diào)用位置抵蚊、內(nèi)部變量等信息都不會(huì)再用到了,只要直接用內(nèi)層函數(shù)的調(diào)用幀溯革,取代外層函數(shù)的調(diào)用幀就可以了贞绳。
換種方式解釋吧
函數(shù)被調(diào)用的時(shí)候會(huì)有函數(shù)執(zhí)行上下文被壓入執(zhí)行棧中,直到函數(shù)執(zhí)行結(jié)束鬓照,對(duì)應(yīng)的執(zhí)行上下文才會(huì)出棧熔酷。
在函數(shù)A的內(nèi)部調(diào)用函數(shù)B孤紧,如果函數(shù)B中有對(duì)函數(shù)A中變量的引用豺裆,那么函數(shù)A即使執(zhí)行結(jié)束對(duì)應(yīng)的執(zhí)行上下文也無(wú)法出棧,如果函數(shù)B內(nèi)部還有調(diào)用函數(shù)C那么要等函數(shù)C執(zhí)行完号显,函數(shù)A臭猜、B對(duì)應(yīng)的執(zhí)行上下文才能出棧,以此類推押蚤,執(zhí)行棧中要上一個(gè)函數(shù)(內(nèi)層函數(shù))的執(zhí)行上下文蔑歌,這就是尾調(diào)用優(yōu)化。
// 尾遞歸
function sampleFn(sample) {
if (sample <= 1) return 1;
return sampleFn(sample - 1) + sample;
}
sampleFn(2);
注意 :
- 當(dāng)內(nèi)層函數(shù)沒(méi)有用到外層函數(shù)的內(nèi)部變量的時(shí)候才可以進(jìn)行尾調(diào)用優(yōu)化揽碘。
- 目前只有 Safari 瀏覽器支持尾調(diào)用優(yōu)化次屠,Chrome 和 Firefox 都不支持。
ES 6 的小修改
函數(shù)參數(shù)尾逗號(hào)
ES2017 允許函數(shù)的最后一個(gè)參數(shù)有尾逗號(hào)(trailing comma)雳刺。
這樣的規(guī)定也使得劫灶,函數(shù)參數(shù)與數(shù)組和對(duì)象的尾逗號(hào)規(guī)則,保持一致了掖桦。
function sampleFn(
sample1,
sample2,
sample3, // 可以在最后一個(gè)參數(shù)后面加 逗號(hào) ','
) {}
toString()修改
Function.prototype.toString()
ES2019 對(duì)函數(shù)實(shí)例的toString()
方法做出了修改本昏。明確要求返回一模一樣的原始代碼。
toString()
方法返回函數(shù)代碼本身枪汪,ES6前會(huì)省略注釋和空格涌穆。
function sampleFn() {
// 注釋
}
let sample = sampleFn.toString();
console.log(sample);
//輸出 完全一樣的原始代碼,包括空格與注釋
/*
function sampleFn() {
// 注釋
}
*/
catch 修改
ES2019 改變了catch
語(yǔ)句后面必須攜帶參數(shù)的要求雀久。允許catch
語(yǔ)句省略參數(shù)宿稀。
try {
// ...
} catch { // 不帶參數(shù)
// ...
}