JavaScript函數(shù)高級(jí)

一、函數(shù)的遞歸
(1) 什么是遞歸
函數(shù)遞歸recursion,描述了函數(shù)在自己的內(nèi)部代碼中調(diào)用自身的過(guò)程
(2) 遞歸基本語(yǔ)法
for循環(huán)

// 1、基本語(yǔ)法:輸出5~1五個(gè)數(shù)據(jù)
// - 普通for循環(huán)
// 理解:
// var i= 5: 循環(huán)變量的初始化,循環(huán)第一次執(zhí)行時(shí)執(zhí)行,并且只執(zhí)行一次
// i > 5:循環(huán)條件,每次進(jìn)入循環(huán)時(shí)需要判斷
// i++:循環(huán)變量修改,每輪循環(huán)結(jié)束時(shí)進(jìn)行變量值修改
for(var i = 5; i > 0; i--) {
  console.log('i-> ', i)
}
// 案例 :for循環(huán)實(shí)現(xiàn)斐波那契數(shù)列 100以?xún)?nèi)
var num1 = num2 = 1 // 初始準(zhǔn)備的兩個(gè)數(shù)據(jù)
console.log("數(shù)列初始數(shù)據(jù):", num1, num2)
var next = 0        // 下一個(gè)數(shù)據(jù)

// 獲取第10位的數(shù)據(jù)
for(var n = 3; n <= 10; n++) {
  // 計(jì)算獲取下一個(gè)數(shù)據(jù)
  next = num1 + num2
  /*if(next >= 100 ) {
    // 如果斐波那契數(shù)列當(dāng)前某個(gè)數(shù)據(jù)大于100,跳出循環(huán)
    break
  }*/
  console.log("-數(shù)列數(shù)據(jù):[" + next + "]")

  // 交換數(shù)據(jù)
  num1 = num2
  num2 = next
}

recursion遞歸
遞歸的本質(zhì):循環(huán)、函數(shù)級(jí)別的循環(huán)

// 2、函數(shù)遞歸:函數(shù)自己調(diào)用自己
// 理解這段代碼:fn()函數(shù)就是用于展示數(shù)據(jù)的,你交給它一個(gè)數(shù)據(jù);它就展示一個(gè)數(shù)據(jù)
// 展示數(shù)據(jù)的函數(shù)
function fn(n) {
  // 結(jié)束條件
  console.log("n計(jì)數(shù):", n)
  if(n == 1) return 1
  // 遞歸調(diào)用:函數(shù)內(nèi)部調(diào)用函數(shù)自己
  // 輸出完當(dāng)前的n,輸出下一個(gè)n:比當(dāng)前的n小于1
  return fn(n-1)
}
// 通過(guò)函數(shù)遞歸的方式,展示數(shù)據(jù)
fn(5)
// 案例:函數(shù)遞歸,獲取100內(nèi)的斐波那契數(shù)列
function feib(n) {       // 參數(shù)n:表示獲取第幾個(gè)位置的數(shù)據(jù)
  if(n <= 2) { // 初始數(shù)據(jù)
    // 表示遞歸結(jié)束的條件: 當(dāng)獲取第一位或者第二位數(shù)據(jù)時(shí),直接返回1
    return 1
  }
  // 下一個(gè)數(shù)據(jù)是前面兩個(gè)數(shù)據(jù)的和:遞歸調(diào)用
  return feib(n-1) + feib(n-2)
}
var r = feib(1) // 獲取第1個(gè)斐波那契數(shù)列的數(shù)據(jù)
console.log('r第一位:',r)
var r = feib(2) // 獲取第2個(gè)斐波那契數(shù)列的數(shù)據(jù)
console.log('r第二位:',r)
var r = feib(10) // 獲取第10個(gè)斐波那契數(shù)列的數(shù)據(jù)
console.log('r第十一位:',r)

(3)函數(shù)在內(nèi)存中執(zhí)行過(guò)程
遞歸執(zhí)行,相當(dāng)于當(dāng)前聲明的函數(shù)在內(nèi)存中一遍又一遍的創(chuàng)建對(duì)象,并執(zhí)行函數(shù)內(nèi)部的代碼,內(nèi)部遞歸的函數(shù)執(zhí)行沒(méi)有完成時(shí),函數(shù)占用的內(nèi)存空間不會(huì)釋放
① 普通函數(shù)的執(zhí)行過(guò)程

// 函數(shù)的聲明部分
function fn() {
  console.log("xxxxxx")
}
// 函數(shù)的調(diào)用執(zhí)行
fn()
image.png

** ② 遞歸函數(shù)在內(nèi)存中的執(zhí)行過(guò)程**

function fn(n) {
  console.log("數(shù)據(jù):", n)
  if(n <= 1) { // 最后當(dāng)n小于等于1的時(shí)候,直接返回1,結(jié)束函數(shù)的繼續(xù)執(zhí)行
    return 1
  }
  // 輸出完當(dāng)前數(shù)據(jù)之后,繼續(xù)輸出下一個(gè)數(shù)據(jù)(比當(dāng)前數(shù)據(jù)小于1的數(shù)據(jù))
  return fn(n - 1)
}
fn(5)   // 5, 4, 3, 2, 1

image.png

二、函數(shù)的閉包
(1) 認(rèn)識(shí)閉包
函數(shù)閉包closure,描述了在一個(gè)函數(shù)的內(nèi)部聲明函數(shù)的語(yǔ)法;擴(kuò)展了外部函數(shù)中局部變量的訪(fǎng)問(wèn)范圍,同時(shí)避免了多功能開(kāi)發(fā)時(shí)的變量全局污染問(wèn)題!

// 函數(shù)的閉包
function outerFn() {// 外部函數(shù)
  // ...
  function innerFn() { // 內(nèi)部函數(shù)(閉包函數(shù)),聲明在外部函數(shù)的內(nèi)部
    // ...
  }
  return innerFn    // 返回內(nèi)部函數(shù),方便函數(shù)的使用
}

(2) 閉包基本語(yǔ)法

// 函數(shù)的閉包:目標(biāo)-先熟悉語(yǔ)法
function outerFn() {
  // 局部變量: 一般都是聲明在函數(shù)內(nèi)部,只在當(dāng)前函數(shù)內(nèi)部可以被訪(fǎng)問(wèn)
  var total = 200
  // 閉包函數(shù)
  function jiSuan(dat) {
    var result = total + dat
    return result
  }
  // 返回了內(nèi)部的閉包函數(shù)(返回的是函數(shù)的聲明)
  return jiSuan
}
// 調(diào)用外部函數(shù),獲取到內(nèi)部函數(shù)
var inFn = outerFn()  // inFn 是一個(gè)函數(shù)
// 繼續(xù)執(zhí)行獲取到的閉包函數(shù):outerFn()函數(shù)的外部,直接操作局部變量的數(shù)據(jù)
var res = inFn(300)
console.log("res:" , res)

function outerFn() {
// ...
// 重點(diǎn):一個(gè)函數(shù)聲明在其他函數(shù)的內(nèi)部 | 特殊的聲明位置
function innerFn() {
 // ...
}
// 返回內(nèi)部的函數(shù)聲明
return innerFn
}
// 調(diào)用:調(diào)用外部函數(shù),獲取到閉包函數(shù)
var inFn = outerFn()    // inFn也是一個(gè)函數(shù)
// 通過(guò)inFn 調(diào)用閉包函數(shù)
var result = inFn()     // 調(diào)用執(zhí)行inFn這個(gè)函數(shù),就是innerFn函數(shù)

(3) 關(guān)于全局污染問(wèn)題
出現(xiàn)了數(shù)據(jù)的互相影響,導(dǎo)致運(yùn)行結(jié)果錯(cuò)誤,這樣的問(wèn)題稱(chēng)為全局污染問(wèn)題。
解決全局污染的方法就是使用閉包

使用閉包,完計(jì)價(jià)器程序的開(kāi)發(fā)
// 聲明一個(gè)計(jì)程車(chē)函數(shù);通過(guò)計(jì)程車(chē)可以獲取到計(jì)價(jià)器
function taxi() {
  // 總價(jià)格:局部變量
  var sum = 0;   
  // 計(jì)價(jià)器
  function jiJiaQi(msg, money) {
    console.log(msg + ":" + money + "元")
    sum += parseInt(money)
    return sum
  }
  // 返回計(jì)價(jià)器
  return jiJiaQi
}
// 執(zhí)行函數(shù),獲取計(jì)程車(chē)的計(jì)價(jià)器
var jjq = taxi()
jjq("起步", 5)
jjq("行駛5公里", 15)
jjq("堵車(chē)", 4)
var s = jjq("行駛3公里", 9)
console.log("達(dá)到目的地:費(fèi)用", s, "元")

三、自執(zhí)行函數(shù)
(1)代碼編寫(xiě)即刻執(zhí)行
代碼寫(xiě)在全局的范圍,只需要寫(xiě)了代碼立即會(huì)執(zhí)行

  • 代碼的執(zhí)行,沒(méi)有問(wèn)題

  • 存在的缺陷:因?yàn)榫W(wǎng)頁(yè)中存在多種效果,可能會(huì)引起全局污染

// 1、代碼直接下載全局范圍,寫(xiě)完代碼就會(huì)立刻執(zhí)行
// 輪播圖效果[模擬]
var effect1 = "輪播圖"
console.log(effect1)
// 選項(xiàng)卡效果
var effect2 = '選項(xiàng)卡'
console.log(effect2)
// 樓層效果
var effect3 = "樓層效果"
console.log(effect3)

(2) 函數(shù)封裝調(diào)用執(zhí)行
將對(duì)應(yīng)的網(wǎng)頁(yè)特效,封裝到多個(gè)獨(dú)立的函數(shù)中,調(diào)用執(zhí)行

  • 每個(gè)網(wǎng)頁(yè)特效的代碼都是 獨(dú)立的函數(shù) 開(kāi)發(fā)
  • 存在的小缺陷:某些特效的函數(shù)代碼,網(wǎng)頁(yè)渲染的過(guò)程中只會(huì)調(diào)用一次,標(biāo)準(zhǔn)的函數(shù)聲明和調(diào)用語(yǔ)法,讓函數(shù)聲明和調(diào)用分散/代碼量增加
// 2、函數(shù)封裝對(duì)應(yīng)的特效,調(diào)用執(zhí)行函數(shù)
function bannerEffect() {
  // 聲明和輪播圖相關(guān)的局部變量
  console.log("輪播圖效果")
}
function tabEffect() {
  // 聲明和選項(xiàng)卡效果相關(guān)的局部變量
  console.log("選項(xiàng)卡效果")
}
function floorEffect() {
  // 聲明和樓層效果相關(guān)的局部變量
  console.log("樓層效果")
}
// 調(diào)用執(zhí)行
bannerEffect()
tabEffect()
floorEffect()

(3) 自執(zhí)行函數(shù)
自執(zhí)行函數(shù),就是描述了函數(shù)在聲明的同時(shí)就立即執(zhí)行的一種語(yǔ)法結(jié)構(gòu)

// 第一種
(function() {...} ())
// 第二種
(function(){...}) ()
// 第三種
!function() {...} ()             
// 第四種
void function() {...}()               
// 第五種
[function() {...}()] 
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容