前言

本章介紹函數(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)聲明的,所以不能用letconst再次聲明。
  • 使用參數(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)寫

  1. 當(dāng)參數(shù)只有一個(gè)時(shí),可以省略箭頭左邊的括號(hào),但沒有參數(shù)時(shí),括號(hào)不可以省略。
  2. 當(dāng)函數(shù)體只有一個(gè)表達(dá)式時(shí),可省略箭頭右邊的大括號(hào),但同時(shí)必須省略return語句 并寫在一行。
  3. 當(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è)概念

  1. 尾調(diào)用
    尾調(diào)用(Tail Call)是函數(shù)式編程的一個(gè)重要概念,就是指某個(gè)函數(shù)的最后一步是調(diào)用另一個(gè)函數(shù)。

  2. 尾遞歸
    函數(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ù)  // ...}