講清楚之 javascript 函數

函數

以前幾節中圍繞着函數梳理了 this、原型鏈、做用域鏈、閉包等內容,這一節梳理一下函數自己的一些特色。javascript

javascript 中函數是一等公民。 而且函數也是對象,由於它們能夠像任何其餘對象同樣具備屬性和方法。它們與其餘對象的區別在於函數能夠被調用。簡而言之,它們是 Function 對象。java

一個函數是能夠經過外部代碼調用的一個「子程序」,函數內部包含了執行語句或表達式。每一個自定義函數都是Function的實例,並繼承Function的全部屬性和方法,而Function 是語言自己提供的編程接口。編程

function foo (name) {
    console.log(`hello, ${name}`)
}
var res = foo(`小明`) // 'hello, 小明'
console.log(res)

調用函數時,傳遞給函數的值被稱爲函數的實參(值傳遞),對應位置的函數參數名叫做形參。若是實參是一個包含原始值(數字,字符串,布爾值)的變量,則就算函數在內部改變了對應形參的值,返回後,該實參變量的值也不會改變。若是實參是一個對象引用,則對應形參會和該實參指向同一個對象。假如函數在內部改變了對應形參的值,返回後,實參指向的對象的值也會改.(歡迎查看參數傳值一節)設計模式

若是函數內部沒有經過return返回一個值,則函數會默認返回undefined.數組

函數在實際開發中承擔着代碼分塊、 功能封裝等任務。瀏覽器

函數定義

// 函數申明
foo()
let a = 1
function foo () {
    console.log(a)
}

// 函數表達式 (函數變量)
let too = function (){
  console.log('hello')
}
too()

// Function 構造函數實例化一個函數
let bar = new Function('console.log("hello")')
bar()

// 箭頭函數
let fns = () => {
    console.log('hello')
}
fns()
須要注意的是 函數申明函數表達式在變量提高時的區別,強烈建議閱讀 變量對象相關內容: 詳情。同時函數申明的 函數名稱不能被改變,而函數表達式賦值給的變量能夠被從新賦值。

函數的各類定義方式就不深刻,這裏須要注意的是經過使用構造函數(new Function())方式建立的函數,在每次調用的時候都會解析一次。因此不推薦使用 Function 構造函數建立函數, 由於它須要的函數體做爲字符串可能會阻止一些 JS 引擎優化,也會引發瀏覽器資源回收等問題。閉包

arguments

argumentsthis同樣是函數提供給函數內部使用的一個屬性(ES6中箭頭函數沒有)。經過arguments咱們能夠獲取調用函數時傳遞進來的實參, 該屬性是一個類數組對象,咱們能夠像數組同樣進行數值的讀取。函數式編程

function foo () {
    let a = arguments[0] // 獲取函數的第一個參數
    let b = arguments[1] // 獲取函數的第二個餐宿
    // 遍歷函數的全部參數
    for (let arg in arguments) {
        console.log(`${arg}: ${arguments[arg]}`)
    }
    arguments[2] = 1122
}

foo(1, 2, 3)

雖然arguments是相似於數組,可是除了經過索引獲取元素和length屬性外,不能使用pushpop等方法。若是確實要進行修改操做,能夠將其轉換爲真正的數組:函數

let args = Array.prototype.slice.call(arguments)
let args = [].slice.call(arguments)

// ES2015
let args = Array.from(arguments)

自執行函數

把函數定義和函數執行結合到一塊兒就是當即執行函數,也叫自執行函數。優化

在官方術語中叫作 IIFE( 當即調用函數表達式),是在定義時就會當即執行的函數。被稱爲自執行匿名函數的設計模式,主要包括兩部分:

  • 第一部分是包圍在 圓括號運算符() 裏的一個匿名函數,這個匿名函數擁有獨立的詞法做用域。這不只避免了外界訪問此 IIFE 中的變量,並且又不會污染全局做用域。借用這個特性咱們能夠對局部功能進行封裝,只暴露不多的方法給外部環境,這也是不少第三方庫的封裝方法之一。
  • 第二部分再一次使用 () 建立了一個當即執行函數表達式,JavaScript 引擎到此將直接執行函數。

當函數變成當即執行的函數表達式時,表達式中的變量不能從外部訪問:

(function () {
    var a = 1
})()
console.log(a) // a is not defined

將 IIFE 分配給一個變量,不是存儲 IIFE 自己,而是存儲 IIFE 執行後返回的結果:

let bar = (function (){
           let a = 123
           return a
           })()
console.log(bar) // 123

別外自執行函數還有一些其餘使用方式:

// 下面2個括弧()都會當即執行
(function(){ /* code */ }());
(function(){ /* code */ })();

// 因爲括弧()和JS的&&,異或,逗號等操做符是在函數表達式和函數聲明上消除歧義的
// 因此一旦解析器知道其中一個已是表達式了,其它的也都默認爲表達式了

var i = function(){ /* code */ }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();

// 若是你不在乎返回值,或者不怕難以閱讀
// 你甚至能夠在function前面加一元操做符號

!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();

// 上面這種使用一元表達式這種方式實際上是不太常見的
// 並且有時候確定在一些場景下存在一些弊端,由於一元表達式會有一個不爲undefined的返回值
// 要想返回值爲undefined,那麼最保險的就是使用void關鍵字

void function(){/* code */}();

()左邊必須是一個函數表達式,而不是一個函數申明。自執行函數也會用在模塊封裝的場景下。

閉包

函數和函數聲明時的詞法做用域造成閉包。咱們可使用閉包來保存變量、封裝不須要暴露的私有屬性和方法。

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}

var foo = checkscope();
foo(); // "local scope"

完全理解閉包的最好途徑就是理解閉包的由來,閉包的產生和做用域鏈有密切關係。在做用域鏈一節已經梳理過閉包的原理。

函數式編程

未完成

相關文章
相關標籤/搜索