JavaScript 編程精解 (3)

第三章 函數(shù)

  • 函數(shù)是JavaScript中不可或缺的組成部分
  • 函數(shù)是構(gòu)造大型程序的工具,可以用于減少重復(fù)性工作、為子程序命名并隔離各個(gè)子程序的運(yùn)行。

3.1 定義函數(shù)

創(chuàng)建函數(shù)的表達(dá)式以關(guān)鍵字function開頭。

var fun = function (parameter1, parameter2, ...) {//參數(shù)可以為空,也可以為一或多個(gè)
    // 函數(shù)體,哪怕只有一條語句,也要包含在大括號中,語句會在調(diào)用函數(shù)時(shí)執(zhí)行
    ...
    // return語句決定了函數(shù)的返回值,后面不跟表達(dá)式時(shí)返回undefined
    return;
}

3.2 參數(shù)和作用域

  • 函數(shù)的參數(shù)初始值由函數(shù)調(diào)用者提供。

  • 函數(shù)內(nèi)部創(chuàng)建的變量和參數(shù)都屬于函數(shù)的局部變量,這種隔離機(jī)制確保了函數(shù)間不會相互干擾。

3.3 嵌套作用域

  • 可以在函數(shù)中創(chuàng)建其他函數(shù),并產(chǎn)生不同程度的局部作用域。

  • 任何局部作用域都可以訪問到包含它的局部作用域

  • 函數(shù)內(nèi)部變量的可見性取決于函數(shù)在代碼當(dāng)中的位置,在包含了一個(gè)函數(shù)定義的代碼塊中,這個(gè)函數(shù)可以訪問到代碼塊中的所有變量,即函數(shù)上層的代碼塊中的變量和函數(shù)內(nèi)部的變量。這種控制變量的方法稱為詞法作用域。

  • let關(guān)鍵字作用與var相同,只不過變量作用域是塊作用域而非函數(shù)局部作用域。

3.4 函數(shù)值

函數(shù)和函數(shù)名的區(qū)別函數(shù)是一種叫做function引用類型的實(shí)例,因此函數(shù)是一個(gè)對象。對象是保存在內(nèi)存中的,函數(shù)名則是指向這個(gè)對象的指針。

JavaScript中函數(shù)是一等公民,可以作為參數(shù)傳入別的函數(shù),也可以作為一個(gè)函數(shù)的返回值,也可以被重新賦值。

3.5 符號聲明

函數(shù)聲明還有一種更為簡潔的方式:

function fun(parameter) {
    //todo
}
  • 函數(shù)聲明不遵循一般的從上到下的流控制規(guī)則。
console.log('are you ok ?', ans());

function ans() {
    return 'yeah, i\'m ok';
}
  • 為了確保函數(shù)在不同環(huán)境下運(yùn)行的行為一致,應(yīng)在最外層的函數(shù)或程序作用域中進(jìn)行函數(shù)聲明。

3.6 調(diào)用棧

由于函數(shù)需要在執(zhí)行結(jié)束后跳轉(zhuǎn)回調(diào)用該函數(shù)的代碼位置,因此計(jì)算機(jī)必須記住函數(shù)調(diào)用的上下文。我們將計(jì)算機(jī)存儲這個(gè)上下文的區(qū)域稱之為調(diào)用棧。

  • 當(dāng)函數(shù)調(diào)用時(shí),當(dāng)前的上下文信息就會被存儲在棧頂

  • 當(dāng)函數(shù)返回時(shí),系統(tǒng)會刪除存儲在棧頂?shù)纳舷挛男畔?,并使用該信息繼續(xù)執(zhí)行程序。

// 若計(jì)算機(jī)空間無限大,循環(huán)調(diào)用會一直執(zhí)行下去,但事實(shí)上是該程序會耗盡內(nèi)存空間,導(dǎo)致“??臻g溢出”。
function chicken() {
    return egg();
}
function egg() {
    return chicken();
}
console.log(chicken() + ' came first.');

3.7 可選參數(shù)

JavaScript對傳送函數(shù)的參數(shù)數(shù)量幾乎不做任何限制。如果你傳遞了過多參數(shù),多余的參數(shù)就會被忽略,而如果你傳遞的參數(shù)過少,遺漏的參數(shù)將會被賦值成undefined。

缺點(diǎn):你可能恰好向函數(shù)傳遞了錯誤數(shù)量的參數(shù),但沒有人會告訴你這個(gè)錯誤。

優(yōu)點(diǎn): 我們可以利用這種行為來讓函數(shù)接收可選參數(shù)。

3.8 閉包

如果函數(shù)已經(jīng)執(zhí)行結(jié)束,那么這些由函數(shù)創(chuàng)建的局部變量會如何處理呢?

function wrapValue(n) {
    var localVariable = n;
    return function { return localVariable; };
}
var wrap1 = wrapValue(1);
var wrap2 = wrapValue(2);
console.log(wrap1());
// 1
console.log(wrap2());
// 2

這段代碼很好地印證了局部變量會在每次函數(shù)調(diào)用時(shí)重新創(chuàng)建,不同的函數(shù)調(diào)用是不會對其他函數(shù)內(nèi)的局部變量產(chǎn)生影響的。

這種引用特定的局部變量實(shí)例的功能稱為閉包。一個(gè)包裝了一些局部變量的函數(shù)是一個(gè)閉包。利用閉包的特性,我們不再需要擔(dān)心變量的生命周期問題,很多高級應(yīng)用都依靠它來實(shí)現(xiàn)。

function multiplier(factor) {
    return function(number) {
        return number * factor;
    }
}

var twice = multiplier(2);
console.log(twice(5));
//10

可以把關(guān)鍵字function當(dāng)做一種“凍結(jié)”代碼并將其打包成函數(shù)值的模型。所以當(dāng)看到“return function(...) {...}”這樣的代碼時(shí),可以將其理解為一個(gè)句柄,其中句柄指向一段包裝好的計(jì)算代碼。

3.9 遞歸

函數(shù)完全可以自己調(diào)用自己,只要避免棧溢出的問題即可。我們把函數(shù)調(diào)用自身的行為稱為遞歸。

function power(base, exponent) {
    if (exponent == 0)
      return 1;
    else
      return base * power(base, exponent-1);
}

console.log(power(2, 3));
// 8

需要注意的是,在標(biāo)準(zhǔn)的JavaScript實(shí)現(xiàn)當(dāng)中,遞歸寫法的函數(shù)執(zhí)行效率比循環(huán)寫法的函數(shù)慢了大約10倍。如何權(quán)衡性能與優(yōu)雅是一個(gè)值得考慮的問題,但有一條基本原則:除非程序執(zhí)行速度確實(shí)太慢,否則先不要關(guān)注效率問題。

對于某些問題來說,遞歸相較于循環(huán)更能解決問題。這類問題通常需要執(zhí)行和處理多個(gè)分支,而每個(gè)分支又會引出更多的執(zhí)行分支。

3.10 添加新函數(shù)

兩種常用的引入函數(shù)的方法:

  1. 找出程序中多次出現(xiàn)的相似代碼。

  2. 寫新功能代碼,覺得一些代碼應(yīng)該包含在一個(gè)函數(shù)時(shí)。甚至可以先編寫調(diào)用函數(shù)的代碼,然后再具體實(shí)現(xiàn)調(diào)用的函數(shù)。

給函數(shù)起名的難易程度取決于我們封裝的函數(shù)的用途是否明確

3.11 函數(shù)及其副作用

可以將函數(shù)分為兩類:一類調(diào)用后產(chǎn)生副作用,而另一類則產(chǎn)生返回值(當(dāng)然也可以定義同時(shí)產(chǎn)生副作用和返回值的函數(shù))。

相比于直接產(chǎn)生副作用的函數(shù),產(chǎn)生返回值的函數(shù)更容易集成到新的環(huán)境當(dāng)中使用。但在副作用的幫助下,有些操作則更易、更快實(shí)現(xiàn),因此考慮到運(yùn)算速度,有時(shí)候純函數(shù)并不可取。

3.12 本章小結(jié)

  1. 對于關(guān)鍵字function來說,當(dāng)我們將其作為表達(dá)式來使用的時(shí)候,可以創(chuàng)建一個(gè)函數(shù)值。當(dāng)我們將其作為語句來使用的時(shí)候,可以用來聲明并將函數(shù)賦予變量。
// Create a function value f
var f = function(a) {
    console.log(a + 2);
}

// Declare g to be a function
function g(a, b) {
    return a * b * 3.5;
}
  1. 要理解函數(shù)的含義,就必須理解局部作用域的概念。對于一個(gè)函數(shù)來說,其參數(shù)及其內(nèi)部聲明的變量都是局部變量,每當(dāng)調(diào)用函數(shù)時(shí),這些變量都會被重新創(chuàng)建,而且對外并不可見。而在函數(shù)作用域當(dāng)中聲明的函數(shù),可以訪問其外部函數(shù)的局部作用域。

  2. 將程序中的任務(wù)劃分到不同的函數(shù)中的做法是非常有用的,而且有助于提高代碼的可讀性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 前言 人生苦多,快來 Kotlin ,快速學(xué)習(xí)Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,699評論 9 118
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,706評論 18 399
  • 與寶寶飯后去買了一本新的note book。不知不覺中他終于完成了第一本日記本。說是日記,也不完全是,不一定是每天...
    無擔(dān)閱讀 253評論 0 0
  • 我發(fā)現(xiàn)每每迷茫的時(shí)候,總是玩手機(jī)到深夜,還常常找成人網(wǎng)站,看完就睡不著了,你有過嗎? 為了音樂夢,我找了間一個(gè)月2...
    楊嘯嘯閱讀 223評論 0 0
  • 很多人都覺得新年年味不重了,可是當(dāng)你一個(gè)人在外面的時(shí)候,你才會明白,新年多么好,可以和家人團(tuán)聚是件多么幸福的事情。...
    切慕彌生閱讀 163評論 0 0

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