縱觀JavaScript中所有必須需要掌握的重點知識中,,函數(shù)是我們在初學的時候最容易忽視的一個知識點。在學習的過程中,,可能會有很多人,、很多文章告訴你面向?qū)ο蠛苤匾秃苤匾?,可是卻很少有人告訴你,,面向?qū)ο笾兴械闹攸c難點,幾乎都與函數(shù)息息相關(guān),。 包括我之前幾篇文章介紹的執(zhí)行上下文,,變量對象,閉包,,this等,,都是圍繞函數(shù)來展開。 我知道很多人在學習中,,很急切的希望自己快一點開始學習面向?qū)ο?,學習模塊,學習流行框架,,然后迅速成為高手,。但是我可以很負責的告訴你,關(guān)于函數(shù)的這些基礎東西沒理解到一定程度,,那么你的學習進展一定是舉步維艱的,。 所以,大家一定要重視函數(shù),! 一,、函數(shù)聲明、函數(shù)表達式,、匿名函數(shù)與自執(zhí)行函數(shù) 關(guān)于函數(shù)在實際開發(fā)中的應用,,大體可以總結(jié)為函數(shù)聲明、函數(shù)表達式,、匿名函數(shù),、自執(zhí)行函數(shù)。 函數(shù)聲明 我們知道,,JavaScript中,,有兩種聲明方式,一個是使用var的變量聲明,,另一個是使用function的函數(shù)聲明,。 在《前端基礎進階(三):變量對象詳解》中我有提到過,,變量對象的創(chuàng)建過程中,函數(shù)聲明比變量聲明具有更為優(yōu)先的執(zhí)行順序,,即我們常常提到的函數(shù)聲明提前,。因此我們在執(zhí)行上下文中,無論在什么位置聲明了函數(shù),,我們都可以在同一個執(zhí)行上下文中直接使用該函數(shù)。
函數(shù)表達式 與函數(shù)聲明不同,,函數(shù)表達式使用了var進行聲明,,那么我們在確認他是否可以正確使用的時候就必須依照var的規(guī)則進行判斷,即變量聲明,。我們知道使用var進行變量聲明,,其實是進行了兩步操作。
同樣的道理,,當我們使用變量聲明的方式來聲明函數(shù)時,就是我們常常說的函數(shù)表達式,。函數(shù)表達的提升方式與變量聲明一致,。
上例子的執(zhí)行順序為:
關(guān)于上面例子中,函數(shù)表達式中的賦值操作,,在其他一些地方也會被經(jīng)常使用,,我們清楚其中的關(guān)系即可。
匿名函數(shù) 在上面我們大概講述了函數(shù)表達式中的賦值操作,。而匿名函數(shù),,顧名思義,就是指的沒有被顯示進行賦值操作的函數(shù),。它的使用場景,,多作為一個參數(shù)傳入另一個函數(shù)中。
在上面的例子中,,fn的第一個參數(shù)傳入了一個匿名函數(shù),。雖然該匿名函數(shù)沒有顯示的進行賦值操作,,我們沒有辦法在外部執(zhí)行上下文中引用到它,但是在fn函數(shù)內(nèi)部,,我們將該匿名函數(shù)賦值給了變量bar,,保存在了fn變量對象的arguments對象中。
由于匿名函數(shù)傳入另一個函數(shù)之后,,最終會在另一個函數(shù)中執(zhí)行,因此我們也常常稱這個匿名函數(shù)為回調(diào)函數(shù),。關(guān)于匿名函數(shù)更多的內(nèi)容,,我會在下一篇深入探討柯里化的文章中進行更加詳細講解。 匿名函數(shù)的這個應用場景幾乎承擔了函數(shù)的所有難以理解的知識點,,因此我們一定要對它的這些細節(jié)了解的足夠清楚,,如果對于變量對象的演變過程你還看不太明白,一定要回過頭去看這篇文章《前端基礎進階(三):變量對象詳解》 函數(shù)自執(zhí)行與塊級作用域 在ES5中,,沒有塊級作用域,,因此我們常常使用函數(shù)自執(zhí)行的方式來模仿塊級作用域,這樣就提供了一個獨立的執(zhí)行上下文,,結(jié)合閉包,,就為模塊化提供了基礎。
一個模塊往往可以包括:私有變量,、私有方法,、公有變量、公有方法,。 根據(jù)作用域鏈的單向訪問,,外面可能很容易知道在這個獨立的模塊中,外部執(zhí)行環(huán)境是無法訪問內(nèi)部的任何變量與方法的,,因此我們可以很容易的創(chuàng)建屬于這個模塊的私有變量與私有方法,。
但是共有方法和變量應該怎么辦?大家還記得我們前面講到過的閉包的特性嗎,?沒錯,,利用閉包,我們可以訪問到執(zhí)行上下文內(nèi)部的變量和方法,,因此,,我們只需要根據(jù)閉包的定義,創(chuàng)建一個閉包,,將你認為需要公開的變量和方法開放出來即可,。
當然,閉包在模塊中的重要作用,,我們也在講解閉包的時候已經(jīng)強調(diào)過,,但是這個知識點真的太重要,需要我們反復理解并且徹底掌握,,因此為了幫助大家進一步理解閉包,,我們來看看jQuery中,是如何利用我們模塊與閉包的,。
在這里,我們只需要看懂閉包與模塊的部分就行了,,至于內(nèi)部的原型鏈是如何繞的,,為什么會這樣寫,我在講面向?qū)ο蟮臅r候會為大家慢慢分析,。舉這個例子的目的所在,,就是希望大家能夠重視函數(shù),因為在實際開發(fā)中,,它無處不在,。 接下來我要分享一個高級的,非常有用的模塊的應用,。當我們的項目越來越大,,那么需要保存的數(shù)據(jù)與狀態(tài)就越來越多,因此,,我們需要一個專門的模塊來維護這些數(shù)據(jù),,這個時候,有一個叫做狀態(tài)管理器的東西就應運而生,。對于狀態(tài)管理器,,最出名的,我想非redux莫屬了。雖然對于還在學習中的大家來說,,redux是一個有點高深莫測的東西,,但是在我們學習之前,可以先通過簡單的方式,,讓大家大致了解狀態(tài)管理器的實現(xiàn)原理,,為我們未來的學習奠定堅實的基礎。 先來直接看代碼,。
demo實例在線地址:http:///yangbo5207/pen/EZzEbY?editors=1111 我之所以說這是一個高級應用,,是因為在單頁應用中,我們很可能會用到這樣的思路,。根據(jù)我們提到過的知識,,理解這個例子其實很簡單,其中的難點估計就在于set方法的處理上,,因為為了具有更多的適用性,,因此做了很多適配,用到了遞歸等知識,。如果你暫時看不懂,,沒有關(guān)系,知道如何使用就行了,,上面的代碼可以直接運用于實際開發(fā),。記住,當你需要保存的狀態(tài)太多的時候,,你就想到這一段代碼就行了,。 函數(shù)自執(zhí)行的方式另外還有其他幾種寫法,諸如!function(){}(),, function(){}() 函數(shù)參數(shù)傳遞方式:按值傳遞 還記得基本數(shù)據(jù)類型與引用數(shù)據(jù)類型在復制上的差異嗎,?基本數(shù)據(jù)類型復制,是直接值發(fā)生了復制,,因此改變后,,各自相互不影響。但是引用數(shù)據(jù)類型的復制,,是保存在變量對象中的引用發(fā)生了復制,,因此復制之后的這兩個引用實際訪問的實際是同一個堆內(nèi)存中的值。當改變其中一個時,,另外一個自然也被改變,。如下例。
當值作為函數(shù)的參數(shù)傳遞進入函數(shù)內(nèi)部時,也有同樣的差異,。我們知道,,函數(shù)的參數(shù)在進入函數(shù)后,實際是被保存在了函數(shù)的變量對象中,,因此,,這個時候相當于發(fā)生了一次復制。如下例,。
正是由于這樣的不同,,導致了許多人在理解函數(shù)參數(shù)的傳遞方式時,就有許多困惑,。到底是按值傳遞還是按引用傳遞,?實際上結(jié)論仍然是按值傳遞,只不過當我們期望傳遞一個引用類型時,,真正傳遞的,,只是這個引用類型保存在變量對象中的引用而已。為了說明這個問題,,我們看看下面這個例子,。
在上面的例子中,如果person是按引用傳遞,,那么person就會自動被修改為指向其name屬性值為Gerg的新對象。但是我們從結(jié)果中看到,,person對象并未發(fā)生任何改變,,因此只是在函數(shù)內(nèi)部引用被修改而已。 函數(shù)式編程 雖然JavaScript并不是一門純函數(shù)式編程的語言,,但是它使用了許多函數(shù)式編程的特性,。因此了解這些特性可以讓我們更加了解自己寫的代碼。 函數(shù)是第一等公民 所謂”第一等公民”(first class),,指的是函數(shù)與其他數(shù)據(jù)類型一樣,,處于平等地位,可以賦值給其他變量,,也可以作為參數(shù),,傳入另一個函數(shù),或者作為別的函數(shù)的返回值,。這些場景,,我們應該見過很多。
只用”表達式”,,不用”語句” “表達式”(expression)是一個單純的運算過程,,總是有返回值;”語句”(statement)是執(zhí)行某種操作,沒有返回值,。函數(shù)式編程要求,,只使用表達式,不使用語句,。也就是說,,每一步都是單純的運算,而且都有返回值,。 了解這一點,,可以讓我們自己在封裝函數(shù)的時候養(yǎng)成良好的習慣。借助這個特性,,我們在學習其他API的時候,,了解函數(shù)的返回值也是一個十分重要的習慣。 沒有”副作用” 所謂”副作用”(side effect),,指的是函數(shù)內(nèi)部與外部互動(最典型的情況,,就是修改全局變量的值),產(chǎn)生運算以外的其他結(jié)果,。 函數(shù)式編程強調(diào)沒有”副作用”,,意味著函數(shù)要保持獨立,所有功能就是返回一個新的值,,沒有其他行為,,尤其是不得修改外部變量的值。 即所謂的只要是同樣的參數(shù)傳入,,返回的結(jié)果一定是相等的,。 閉包 閉包是函數(shù)式編程語言的重要特性,我也在前面幾篇文章中說了很多關(guān)于閉包的內(nèi)容,。這里不再贅述,。 柯里化 理解柯里化稍微有點難,我在下一篇文章里專門單獨來深入分析,。 函數(shù)封裝 在我們自己封裝函數(shù)時,,最好盡量根據(jù)函數(shù)式編程的特點來編寫。當然在許多情況下并不能完全做到,,比如函數(shù)中我們常常會利用模塊中的私有變量等,。 普通封裝
掛載在對象上
修改數(shù)組對象的例子,常在面試中被問到類似的,,但是并不建議在實際開發(fā)中擴展原生對象,。與普通封裝不一樣的是,因為掛載在對象的原型上我們可以通過this來訪問對象的屬性和方法,,所以這種封裝在實際使用時會有許多的難點,,因此我們一定要掌握好this,。 本系列: |
|