前言
本章介紹函數(shù)的擴(kuò)展。有些不常用的知識(shí)了解即可。
本章原文鏈接:函數(shù)的擴(kuò)展。
函數(shù)參數(shù)的默認(rèn)值
ES6 允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值,即直接寫在參數(shù)定義的后面。
當(dāng)函數(shù)形參沒有被賦值時(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
屬性返回的是沒有指定默認(rèn)值的參數(shù)的個(gè)數(shù)。 - 參數(shù)的默認(rèn)值一旦設(shè)定,函數(shù)進(jìn)行聲明初始化時(shí),參數(shù)會(huì)形成一個(gè)多帶帶的作用域(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è)多帶帶的作用域,等到初始化結(jié)束,該作用域就會(huì)消失。而且該行為只在函數(shù)參數(shù)指定了默認(rèn)值才會(huì)出現(xiàn)。
let sample = 1;/* 在聲明的時(shí)候出現(xiàn)多帶帶作用域 在這個(gè)作用域中,變量沒有定義,于是指向外層變量 函數(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); // sampleconsole.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ù)沒有prototype
。
// 普通函數(shù)let sample = function (item) { return item;};// 上面函數(shù)等同于下面函數(shù)// 使用箭頭函數(shù)let sample = (item) => { return item}; // 箭頭函數(shù)
箭頭函數(shù)簡(jiǎn)寫
沒錯(cuò),箭頭函數(shù)還可以簡(jiǎn)寫
- 當(dāng)參數(shù)只有一個(gè)時(shí),可以省略箭頭左邊的括號(hào),但沒有參數(shù)時(shí),括號(hào)不可以省略。
- 當(dāng)函數(shù)體只有一個(gè)表達(dá)式時(shí),可省略箭頭右邊的大括號(hào),但同時(shí)必須省略
return
語句 并寫在一行。 - 當(dāng)函數(shù)體分多于一條語句,就要使用大括號(hào)將它們括起來,并且使用
return
語句返回。
// 下面幾種函數(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)和 returnlet 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()
方法無效。
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)化”。
這就是說,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í)行上下文也無法出棧,如果函數(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ù)沒有用到外層函數(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
語句后面必須攜帶參數(shù)的要求。允許catch
語句省略參數(shù)。
try { // ...} catch { // 不帶參數(shù) // ...}