JavaScript的函數和做用域閉包

1. 函數

1.1 定義函數

function add(x, y){
    return x + y;
}

上述函數定義以下:javascript

  • 關鍵字function指出這是一個函數定義;
  • add是函數的名稱;
  • (x, y)括號內列出函數的參數,多個參數以,分隔;
  • {}之間的代碼是函數體,能夠包含若干語句,甚至能夠沒有任何語句。

函數體內部的語句在執行時,一旦執行到return時,函數就執行完畢,並將結果返回。
若是沒有return語句,函數執行完畢後也會返回結果,只是結果爲undefinedhtml

JavaScript的函數也是一個對象,函數名能夠視爲指向該函數的變量。所以,函數也能夠像下面這樣定義。java

var add = function (x, y){
    return x + y;
}

這種狀況下,function (x, y){}是一個匿名函數,它沒有函數名。可是,這個匿名函數賦值給了變量add,因此,經過變量add就能夠調用該函數。數組

1.2 調用函數

調用函數時,按順序傳入參數便可。閉包

add(1, 2); // 3

關鍵字arguments只在函數內部起做用。咱們經過arguments能夠得到調用者傳入的全部參數。事實上,arguments最經常使用於判斷傳入參數的個數。app

function add(x, y){
    for(let i = 0; i < arguments.length; i++){
        console.log(arguments[i]);
    }
    return x + y;
}

console.log(add(1,5)); // 1 5 6

2. 做用域

2.1 概述

在JavaScript中,用var聲明的變量是有做用域的。
若是一個變量在函數體內聲明,則該變量的做用域是整個函數體。
若是兩個不一樣的函數各自聲明瞭一個同名變量,那麼這兩個變量是相互獨立的,互不影響。
因爲JavaScript的函數能夠嵌套,內部函數能夠訪問外部函數定義的變量,反過來則不行。
JavaScript的函數在查找變量時從自身函數定義開始,從"內"向"外"查找。
若是內部函數定義了與外部函數重名的變量,則內部函數的變量將"屏蔽"外部函數的變量。函數

2.2 變量提高

JavaScript會把變量聲明提高到頂部。post

function foo(){
    console.log(x); // 不報錯
    var x = 'Hello';
}

// 等同於

function foo(){
    var x;
    console.log(x);
    x = 'Hello';
}

3. 方法

在一個對象中綁定函數,稱爲這個對象的方法。學習

var me = {
    name: '張三',
    birth: 1994,
    age: function(){
        var y = new Date().getFullYear();
        return y - this.birth;
    }
};

console.log(me.age()); // 25

若是以對象的方法形式調用me.age(),函數age()this指被調用的對象me網站

3.1 applycallbind

var obj = {birth: 1990};

console.log(me.age.call(obj)); // 29
console.log(me.age.apply(obj)); // 29
let bind = me.age.bind(obj);
console.log(bind()); // 29

applycallbind這三個函數的做用是改變函數執行時的上下文,即改變函數運行時的this的指向。

  • applycall改變了函數的this上下文後便執行該函數,bind則返回改變了上下文後的一個函數。
  • applycall的第一個參數都是要改變上下文的對象,而call從第二個參數開始以參數列表的形式展示,apply把除了改變上下文對象的參數放在一個數組裏面做爲它的第二個參數。
let arr1 = [1, 2, 10, 4];
//例子:求數組中的最值
console.log(Math.max.call(null, 1, 2, 10, 4)); // 10
console.log(Math.max.call(null, arr1)); // NaN
console.log(Math.max.apply(null, arr1)); // 10

5. 高階函數

5.1 概述

JavaScript的函數接收變量做爲參數,而函數名能夠視爲指向該函數的變量。所以,一個函數能夠接收另外一個函數做爲參數。這種函數被稱爲高階函數

function add(x, y, f) {
    return f(x) + f(y);
}

console.log(add(-5, 6, Math.abs)); // 11

內置對象Array提供了一些高階函數:

  • map
  • reduce
  • filter
  • sort
  • every
  • find
  • findIndex
  • forEach

相關閱讀JavaScript Array 對象

5.2 閉包

高階函數除了能夠接受函數做爲參數外,還能夠把函數做爲結果值返回。

/*      
function lazy_sum(arr) {
    var sum = function () {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    }
    return sum;
}
*/
function lazy_sum(arr) {
    return function () {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    };
}

var f = lazy_sum([1, 2, 3, 4, 5]);
console.log(f()); // 15

lazy_sum函數中定義了函數sumsum函數能夠訪問lazy_sum函數的變量arr(內部函數能夠訪問外部函數,參數能夠看做函數中聲明的變量)。當一個函數返回了一個函數後,相關的變量都保存在返回的函數中。
返回的sum函數依然保持對lazy_sum函數的做用域的引用,這個引用就叫做閉包
注意:返回的函數沒有當即執行,而是直到調用了f()才執行。
使用閉包時要牢記一點是:返回函數不要引用任何循環變量,或者後續會發生變化的變量。

再看另外一個例子。

var buildMultiplier = function(x) {
    return function(y) {
        return x * y;
    }
}

var double = buildMultiplier(2);
var triple = buildMultiplier(3);

console.log(double(3)); // 6
console.log(triple(3)); // 9

參考

相關文章
相關標籤/搜索