函數與函數式編程

函數與函數式編程

 

縱觀JavaScript中全部必須須要掌握的重點知識中,函數是咱們在初學的時候最容易忽視的一個知識點。在學習的過程當中,可能會有不少人、不少文章告訴你面向對象很重要,原型很重要,但是卻不多有人告訴你,面向對象中全部的重點難點,幾乎都與函數息息相關。css

包括我以前幾篇文章介紹的執行上下文,變量對象,閉包,this等,都是圍繞函數來展開。react

我知道不少人在學習中,很急切的但願本身快一點開始學習面向對象,學習模塊,學習流行框架,而後迅速成爲高手。可是我能夠很負責的告訴你,關於函數的這些基礎東西沒理解到必定程度,那麼你的學習進展必定是舉步維艱的。git

因此,你們必定要重視函數!github

1、函數聲明、函數表達式、匿名函數與自執行函數

關於函數在實際開發中的應用,大致能夠總結爲函數聲明、函數表達式、匿名函數、自執行函數。面試

函數聲明express

咱們知道,JavaScript中,有兩種聲明方式,一個是使用var的變量聲明,另外一個是使用function的函數聲明。編程

變量對象的建立過程當中,函數聲明比變量聲明具備更爲優先的執行順序,即咱們經常提到的函數聲明提早。所以咱們在執行上下文中,不管在什麼位置聲明瞭函數,咱們均可以在同一個執行上下文中直接使用該函數。redux

函數表達式小程序

與函數聲明不一樣,函數表達式使用了var進行聲明,那麼咱們在確認他是否能夠正確使用的時候就必須依照var的規則進行判斷,即變量聲明。咱們知道使用var進行變量聲明,實際上是進行了兩步操做。數組

一樣的道理,當咱們使用變量聲明的方式來聲明函數時,就是咱們經常說的函數表達式。函數表達的提高方式與變量聲明一致。

上例子的執行順序爲:

 

所以,因爲聲明方式的不一樣,致使了函數聲明與函數表達式在使用上的一些差別須要咱們注意,除此以外,這兩種形式的函數在使用上並沒有不一樣。

關於上面例子中,函數表達式中的賦值操做,在其餘一些地方也會被常用,咱們清楚其中的關係便可。

匿名函數

在上面咱們大概講述了函數表達式中的賦值操做。而匿名函數,顧名思義,就是指的沒有被顯示進行賦值操做的函數。它的使用場景,多做爲一個參數傳入另外一個函數中。

在上面的例子中,fn的第一個參數傳入了一個匿名函數。雖然該匿名函數沒有顯示的進行賦值操做,咱們沒有辦法在外部執行上下文中引用到它,可是在fn函數內部,咱們將該匿名函數賦值給了變量bar,保存在了fn變量對象的arguments對象中。

因爲匿名函數傳入另外一個函數以後,最終會在另外一個函數中執行,所以咱們也經常稱這個匿名函數爲回調函數。關於匿名函數更多的內容,我會在下一篇深刻探討柯里化的文章中進行更加詳細講解。

匿名函數的這個應用場景幾乎承擔了函數的全部難以理解的知識點,所以咱們必定要對它的這些細節瞭解的足夠清楚。

 

函數自執行與塊級做用域

在ES5中,沒有塊級做用域,所以咱們經常使用函數自執行的方式來模仿塊級做用域,這樣就提供了一個獨立的執行上下文,結合閉包,就爲模塊化提供了基礎。

一個模塊每每能夠包括:私有變量、私有方法、公有變量、公有方法。

根據做用域鏈的單向訪問,外面可能很容易知道在這個獨立的模塊中,外部執行環境是沒法訪問內部的任何變量與方法的,所以咱們能夠很容易的建立屬於這個模塊的私有變量與私有方法。

可是共有方法和變量應該怎麼辦?你們還記得咱們前面講到過的閉包的特性嗎?沒錯,利用閉包,咱們能夠訪問到執行上下文內部的變量和方法,所以,咱們只須要根據閉包的定義,建立一個閉包,將你認爲須要公開的變量和方法開放出來便可。

 

固然,閉包在模塊中的重要做用,咱們也在講解閉包的時候已經強調過,可是這個知識點真的過重要,須要咱們反覆理解而且完全掌握,所以爲了幫助你們進一步理解閉包,咱們來看看jQuery中,是如何利用咱們模塊與閉包的。

在這裏,咱們只須要看懂閉包與模塊的部分就好了,至於內部的原型鏈是如何繞的,爲何會這樣寫,我在講面向對象的時候會爲你們慢慢分析。舉這個例子的目的所在,就是但願你們可以重視函數,由於在實際開發中,它無處不在。

接下來我要分享一個高級的,很是有用的模塊的應用。當咱們的項目愈來愈大,那麼須要保存的數據與狀態就愈來愈多,所以,咱們須要一個專門的模塊來維護這些數據,這個時候,有一個叫作狀態管理器的東西就應運而生。對於狀態管理器,最出名的,我想非redux莫屬了。雖然對於還在學習中的你們來講,redux是一個有點高深莫測的東西,可是在咱們學習以前,能夠先經過簡單的方式,讓你們大體瞭解狀態管理器的實現原理,爲咱們將來的學習奠基堅實的基礎。

先來直接看代碼。

我之因此說這是一個高級應用,是由於在單頁應用中,咱們極可能會用到這樣的思路。根據咱們提到過的知識,理解這個例子其實很簡單,其中的難點估計就在於set方法的處理上,由於爲了具備更多的適用性,所以作了不少適配,用到了遞歸等知識。若是你暫時看不懂,沒有關係,知道如何使用就好了,上面的代碼能夠直接運用於實際開發。記住,當你須要保存的狀態太多的時候,你就想到這一段代碼就好了。

函數自執行的方式另外還有其餘幾種寫法,諸如!function(){}()+function(){}()

2、函數參數傳遞方式:按值傳遞

還記得基本數據類型與引用數據類型在複製上的差別嗎?基本數據類型複製,是直接值發生了複製,所以改變後,各自相互不影響。可是引用數據類型的複製,是保存在變量對象中的引用發生了複製,所以複製以後的這兩個引用實際訪問的實際是同一個堆內存中的值。當改變其中一個時,另一個天然也被改變。以下例。

當值做爲函數的參數傳遞進入函數內部時,也有一樣的差別。咱們知道,函數的參數在進入函數後,實際是被保存在了函數的變量對象中,所以,這個時候至關於發生了一次複製。以下例。

 

正是因爲這樣的不一樣,致使了許多人在理解函數參數的傳遞方式時,就有許多困惑。究竟是按值傳遞仍是按引用傳遞?實際上結論仍然是按值傳遞,只不過當咱們指望傳遞一個引用類型時,真正傳遞的,只是這個引用類型保存在變量對象中的引用而已。爲了說明這個問題,咱們看看下面這個例子。

在上面的例子中,若是person是按引用傳遞,那麼person就會自動被修改成指向其name屬性值爲Gerg的新對象。可是咱們從結果中看到,person對象並未發生任何改變,所以只是在函數內部引用被修改而已。

4、函數式編程

雖然JavaScript並非一門純函數式編程的語言,可是它使用了許多函數式編程的特性。所以瞭解這些特性可讓咱們更加了解本身寫的代碼。

函數是第一等公民

所謂」第一等公民」(first class),指的是函數與其餘數據類型同樣,處於平等地位,能夠賦值給其餘變量,也能夠做爲參數,傳入另外一個函數,或者做爲別的函數的返回值。這些場景,咱們應該見過不少。

只用」表達式」,不用」語句」

「表達式」(expression)是一個單純的運算過程,老是有返回值;」語句」(statement)是執行某種操做,沒有返回值。函數式編程要求,只使用表達式,不使用語句。也就是說,每一步都是單純的運算,並且都有返回值。

瞭解這一點,可讓咱們本身在封裝函數的時候養成良好的習慣。藉助這個特性,咱們在學習其餘API的時候,瞭解函數的返回值也是一個十分重要的習慣。

沒有」反作用」

所謂」反作用」(side effect),指的是函數內部與外部互動(最典型的狀況,就是修改全局變量的值),產生運算之外的其餘結果。

函數式編程強調沒有」反作用」,意味着函數要保持獨立,全部功能就是返回一個新的值,沒有其餘行爲,尤爲是不得修改外部變量的值。

即所謂的只要是一樣的參數傳入,返回的結果必定是相等的。

閉包

閉包是函數式編程語言的重要特性,我也在前面幾篇文章中說了不少關於閉包的內容。這裏再也不贅述。

柯里化

理解柯里化稍微有點難,我在下一篇文章裏專門單獨來深刻分析。

5、函數封裝

在咱們本身封裝函數時,最好儘可能根據函數式編程的特色來編寫。固然在許多狀況下並不能徹底作到,好比函數中咱們經常會利用模塊中的私有變量等。

普通封裝

掛載在對象上

修改數組對象的例子,常在面試中被問到相似的,可是並不建議在實際開發中擴展原生對象。與普通封裝不同的是,由於掛載在對象的原型上咱們能夠經過this來訪問對象的屬性和方法,因此這種封裝在實際使用時會有許多的難點,所以咱們必定要掌握好this。

相關文章
相關標籤/搜索