国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

前端基礎(chǔ)進(jìn)階(七):函數(shù)與函數(shù)式編程

GeekGhc / 3341人閱讀

摘要:一函數(shù)聲明函數(shù)表達(dá)式匿名函數(shù)與自執(zhí)行函數(shù)關(guān)于函數(shù)在實(shí)際開發(fā)中的應(yīng)用,大體可以總結(jié)為函數(shù)聲明函數(shù)表達(dá)式匿名函數(shù)自執(zhí)行函數(shù)。而匿名函數(shù),顧名思義,就是指的沒有被顯示進(jìn)行賦值操作的函數(shù)。而函數(shù)自執(zhí)行,其實(shí)是匿名函數(shù)的一種應(yīng)用。

縱觀JavaScript中所有必須需要掌握的重點(diǎn)知識中,函數(shù)是我們在初學(xué)的時候最容易忽視的一個知識點(diǎn)。在學(xué)習(xí)的過程中,可能會有很多人、很多文章告訴你面向?qū)ο蠛苤匾秃苤匾墒菂s很少有人告訴你,面向?qū)ο笾兴械闹攸c(diǎn)難點(diǎn),幾乎都與函數(shù)息息相關(guān)。

包括我之前幾篇文章介紹的執(zhí)行上下文,變量對象,閉包,this等,都是圍繞函數(shù)的細(xì)節(jié)來展開。

我知道很多人在學(xué)習(xí)中,很急切的希望自己快一點(diǎn)開始學(xué)習(xí)面向?qū)ο螅瑢W(xué)習(xí)模塊,學(xué)習(xí)流行框架,然后迅速成為高手。但是我可以很負(fù)責(zé)的告訴你,關(guān)于函數(shù)的這些基礎(chǔ)東西沒理解到一定程度,那么你的學(xué)習(xí)進(jìn)展一定是舉步維艱的。

所以,大家一定要重視函數(shù)!

當(dāng)然,關(guān)于函數(shù)的重點(diǎn),難點(diǎn)在前面幾篇文章都已經(jīng)說得差不多了,這篇文章主要總結(jié)一下函數(shù)的基礎(chǔ)知識,并初步學(xué)習(xí)函數(shù)式編程的思維。

一、函數(shù)聲明、函數(shù)表達(dá)式、匿名函數(shù)與自執(zhí)行函數(shù)

關(guān)于函數(shù)在實(shí)際開發(fā)中的應(yīng)用,大體可以總結(jié)為函數(shù)聲明、函數(shù)表達(dá)式、匿名函數(shù)、自執(zhí)行函數(shù)。

函數(shù)聲明

我們知道,JavaScript中,有兩種聲明方式,一個是使用var的變量聲明,另一個是使用function的函數(shù)聲明。

在前端基礎(chǔ)進(jìn)階(三):變量對象詳解中我有提到過,變量對象的創(chuàng)建過程中,函數(shù)聲明比變量聲明具有更為優(yōu)先的執(zhí)行順序,即我們常常提到的函數(shù)聲明提前。因此我們在執(zhí)行上下文中,無論在什么位置聲明了函數(shù),我們都可以在同一個執(zhí)行上下文中直接使用該函數(shù)。

fn();  // function

function fn() {
    console.log("function");
}

函數(shù)表達(dá)式

與函數(shù)聲明不同,函數(shù)表達(dá)式使用了var進(jìn)行聲明,那么我們在確認(rèn)他是否可以正確使用的時候就必須依照var的規(guī)則進(jìn)行判斷,即變量聲明。我們知道使用var進(jìn)行變量聲明,其實(shí)是進(jìn)行了兩步操作。

// 變量聲明
var a = 20;

// 實(shí)際執(zhí)行順序
var a = undefined;  // 變量聲明,初始值undefined,變量提升,提升順序次于function聲明
a = 20;  // 變量賦值,該操作不會提升

同樣的道理,當(dāng)我們使用變量聲明的方式來聲明函數(shù)時,就是我們常常說的函數(shù)表達(dá)式。函數(shù)表達(dá)的提升方式與變量聲明一致。

fn(); // 報(bào)錯
var fn = function() {
    console.log("function");
}

上例子的執(zhí)行順序?yàn)椋?/p>

var fn = undefined;   // 變量聲明提升
fn();    // 執(zhí)行報(bào)錯
fn = function() {   // 賦值操作,此時將后邊函數(shù)的引用賦值給fn
    console.log("function");
}
因此,由于聲明方式的不同,導(dǎo)致了函數(shù)聲明與函數(shù)表達(dá)式在使用上的一些差異需要我們注意,除此之外,這兩種形式的函數(shù)在使用上并無不同。

關(guān)于上面例子中,函數(shù)表達(dá)式中的賦值操作,在其他一些地方也會被經(jīng)常使用,我們清楚其中的關(guān)系即可。

在構(gòu)造函數(shù)中添加方法
function Person(name) {
    this.name = name;
    this.age = age;
    // 在構(gòu)造函數(shù)內(nèi)部中添加方法
    this.getAge = function() {
        return this.age;
    }
    this.
}
// 給原型添加方法
Person.prototype.getName = function() {
    return this.name;
}

// 在對象中添加方法
var a = {
    m: 20,
    getM: function() {
        return this.m;
    }
}

匿名函數(shù)

在上面我們大概講述了函數(shù)表達(dá)式中的賦值操作。而匿名函數(shù),顧名思義,就是指的沒有被顯示進(jìn)行賦值操作的函數(shù)。它的使用場景,多作為一個參數(shù)傳入另一個函數(shù)中。

var a = 10;
var fn = function(bar, num) {
    return bar() + num;
}

fn(function() {
    return a;
}, 20)

在上面的例子中,fn的第一個參數(shù)傳入了一個匿名函數(shù)。雖然該匿名函數(shù)沒有顯示的進(jìn)行賦值操作,我們沒有辦法在外部執(zhí)行上下文中引用到它,但是在fn函數(shù)內(nèi)部,我們將該匿名函數(shù)賦值給了變量bar,保存在了fn變量對象的arguments對象中。

// 變量對象在fn上下文執(zhí)行過程中的創(chuàng)建階段
VO(fn) = {
    arguments: {
        bar: undefined,
        num: undefined,
        length: 2
    }
}

// 變量對象在fn上下文執(zhí)行過程中的執(zhí)行階段
// 變量對象變?yōu)榛顒訉ο螅⑼瓿少x值操作與執(zhí)行可執(zhí)行代碼
VO -> AO

AO(fn) = {
    arguments: {
        bar: function() { return a },
        num: 20,
        length: 2
    }
}

由于匿名函數(shù)傳入另一個函數(shù)之后,最終會在另一個函數(shù)中執(zhí)行,因此我們也常常稱這個匿名函數(shù)為回調(diào)函數(shù)。關(guān)于匿名函數(shù)更多的內(nèi)容,我會在下一篇深入探討柯里化的文章中進(jìn)行更加詳細(xì)講解。

匿名函數(shù)的這個應(yīng)用場景幾乎承擔(dān)了函數(shù)的所有難以理解的知識點(diǎn),因此我們一定要對它的這些細(xì)節(jié)了解的足夠清楚,如果對于變量對象的演變過程你還看不太明白,一定要回過頭去看這篇文章:前端基礎(chǔ)進(jìn)階(三):變量對象詳解

函數(shù)自執(zhí)行與塊級作用域

在ES5中,沒有塊級作用域,因此我們常常使用函數(shù)自執(zhí)行的方式來模仿塊級作用域,這樣就提供了一個獨(dú)立的執(zhí)行上下文,結(jié)合閉包,就為模塊化提供了基礎(chǔ)。而函數(shù)自執(zhí)行,其實(shí)是匿名函數(shù)的一種應(yīng)用。

(function() {
   // ...
})();

一個模塊往往可以包括:私有變量、私有方法、公有變量、公有方法。

根據(jù)作用域鏈的單向訪問,外面可能很容易知道在這個獨(dú)立的模塊中,外部執(zhí)行環(huán)境是無法訪問內(nèi)部的任何變量與方法的,因此我們可以很容易的創(chuàng)建屬于這個模塊的私有變量與私有方法。

(function() {
    // 私有變量
    var age = 20;
    var name = "Tom";

    // 私有方法
    function getName() {
        return `your name is ` + name;
    }
})();

但是共有方法和變量應(yīng)該怎么辦?大家還記得我們前面講到過的閉包的特性嗎?沒錯,利用閉包,我們可以訪問到執(zhí)行上下文內(nèi)部的變量和方法,因此,我們只需要根據(jù)閉包的定義,創(chuàng)建一個閉包,將你認(rèn)為需要公開的變量和方法開放出來即可。

如果你對閉包了解不夠,前端基礎(chǔ)進(jìn)階(四):詳細(xì)圖解作用域鏈與閉包應(yīng)該可以幫到你。
(function() {
    // 私有變量
    var age = 20;
    var name = "Tom";

    // 私有方法
    function getName() {
        return `your name is ` + name;
    }

    // 共有方法
    function getAge() {
        return age;
    }

    // 將引用保存在外部執(zhí)行環(huán)境的變量中,形成閉包,防止該執(zhí)行環(huán)境被垃圾回收
    window.getAge = getAge;
})();

當(dāng)然,閉包在模塊中的重要作用,我們也在講解閉包的時候已經(jīng)強(qiáng)調(diào)過,但是這個知識點(diǎn)真的太重要,需要我們反復(fù)理解并且徹底掌握,因此為了幫助大家進(jìn)一步理解閉包,我們來看看jQuery中,是如何利用我們模塊與閉包的。

// 使用函數(shù)自執(zhí)行的方式創(chuàng)建模塊
(function(window, undefined) {

    // 聲明jQuery構(gòu)造函數(shù)
     var jQuery = function(name) {

        // 主動在構(gòu)造函數(shù)中,返回一個jQuery實(shí)例
         return new jQuery.fn.init(name);
     }

    // 添加原型方法
     jQuery.prototype = jQuery.fn = {
         constructor: jQuery,
         init:function() { ... },
         css: function() { ... }
     }
     jQuery.fn.init.prototype = jQuery.fn;

    // 將jQuery改名為$,并將引用保存在window上,形成閉包,對外開發(fā)jQuery構(gòu)造函數(shù),這樣我們就可以訪問所有掛載在jQuery原型上的方法了
     window.jQuery = window.$ = jQuery;
 })(window);

// 在使用時,我們直接執(zhí)行了構(gòu)造函數(shù),因?yàn)樵趈Query的構(gòu)造函數(shù)中通過一些手段,返回的是jQuery的實(shí)例,所以我們就不用再每次用的時候在自己new了
$("#div1");

在這里,我們只需要看懂閉包與模塊的部分就行了,至于內(nèi)部的原型鏈?zhǔn)侨绾卫@的,為什么會這樣寫,我在講面向?qū)ο蟮臅r候會為大家慢慢分析。舉這個例子的目的所在,就是希望大家能夠重視函數(shù),因?yàn)樵趯?shí)際開發(fā)中,它無處不在。

接下來我要分享一個高級的,非常有用的模塊的應(yīng)用。當(dāng)我們的項(xiàng)目越來越大,那么需要保存的數(shù)據(jù)與狀態(tài)就越來越多,因此,我們需要一個專門的模塊來維護(hù)這些數(shù)據(jù),這個時候,有一個叫做狀態(tài)管理器的東西就應(yīng)運(yùn)而生。對于狀態(tài)管理器,最出名的,我想非redux莫屬了。雖然對于還在學(xué)習(xí)中的大家來說,redux是一個有點(diǎn)高深莫測的東西,但是在我們學(xué)習(xí)之前,可以先通過簡單的方式,讓大家大致了解狀態(tài)管理器的實(shí)現(xiàn)原理,為我們未來的學(xué)習(xí)奠定堅(jiān)實(shí)的基礎(chǔ)。

先來直接看代碼。

// 自執(zhí)行創(chuàng)建模塊
(function() {
    // states 結(jié)構(gòu)預(yù)覽
    // states = {
    //     a: 1,
    //     b: 2,
    //     m: 30,  
    //     o: {}
    // }
    var states = {};  // 私有變量,用來存儲狀態(tài)與數(shù)據(jù)

    // 判斷數(shù)據(jù)類型
    function type(elem) {
        if(elem == null) {
            return elem + "";
        }
        return toString.call(elem).replace(/[[]]/g, "").split(" ")[1].toLowerCase();
    }


    /**
     * @Param name 屬性名
     * @Description 通過屬性名獲取保存在states中的值
    */
    function get(name) {
        return states[name] ? states[name] : "";
    }

    function getStates() {
        return states;
    }

    /*
    * @param options {object} 鍵值對
    * @param target {object} 屬性值為對象的屬性,只在函數(shù)實(shí)現(xiàn)時遞歸中傳入
    * @desc 通過傳入鍵值對的方式修改state樹,使用方式與小程序的data或者react中的setStates類似
    */
    function set(options, target) {
        var keys = Object.keys(options);
        var o = target ? target : states;

        keys.map(function(item) {
            if(typeof o[item] == "undefined") {
                o[item] = options[item];
            }
            else {
                type(o[item]) == "object" ? set(options[item], o[item]) : o[item] = options[item];
            }
            return item;
        })
    }

    // 對外提供接口
    window.get = get;
    window.set = set;
    window.getStates = getStates;
})()

// 具體使用如下

set({ a: 20 });     // 保存 屬性a
set({ b: 100 });    // 保存屬性b
set({ c: 10 });     // 保存屬性c

// 保存屬性o, 它的值為一個對象
set({
    o: {
        m: 10,
        n: 20
    }
})

// 修改對象o 的m值
set({
    o: {
        m: 1000
    }
})

// 給對象o中增加一個c屬性
set({
    o: {
        c: 100
    }
})
console.log(getStates())

demo實(shí)例在線地址

我之所以說這是一個高級應(yīng)用,是因?yàn)樵趩雾搼?yīng)用中,我們很可能會用到這樣的思路。根據(jù)我們提到過的知識,理解這個例子其實(shí)很簡單,其中的難點(diǎn)估計(jì)就在于set方法的處理上,因?yàn)闉榱司哂懈嗟倪m用性,因此做了很多適配,用到了遞歸等知識。如果你暫時看不懂,沒有關(guān)系,知道如何使用就行了,上面的代碼可以直接運(yùn)用于實(shí)際開發(fā)。記住,當(dāng)你需要保存的狀態(tài)太多的時候,你就想到這一段代碼就行了。

函數(shù)自執(zhí)行的方式另外還有其他幾種寫法,諸如!function(){}()+function(){}()
二、函數(shù)參數(shù)傳遞方式:按值傳遞

還記得基本數(shù)據(jù)類型與引用數(shù)據(jù)類型在復(fù)制上的差異嗎?基本數(shù)據(jù)類型復(fù)制,是直接值發(fā)生了復(fù)制,因此改變后,各自相互不影響。但是引用數(shù)據(jù)類型的復(fù)制,是保存在變量對象中的引用發(fā)生了復(fù)制,因此復(fù)制之后的這兩個引用實(shí)際訪問的實(shí)際是同一個堆內(nèi)存中的值。當(dāng)改變其中一個時,另外一個自然也被改變。如下例。

var a = 20;
var b = a;
b = 10;
console.log(a);  // 20

var m = { a: 1, b: 2 }
var n = m;
n.a = 5;
console.log(m.a) // 5

當(dāng)值作為函數(shù)的參數(shù)傳遞進(jìn)入函數(shù)內(nèi)部時,也有同樣的差異。我們知道,函數(shù)的參數(shù)在進(jìn)入函數(shù)后,實(shí)際是被保存在了函數(shù)的變量對象中,因此,這個時候相當(dāng)于發(fā)生了一次復(fù)制。如下例。

var a = 20;

function fn(a) {
    a = a + 10;
    return a;
}
fn(a);
console.log(a); // 20
var a = { m: 10, n: 20 }
function fn(a) {
    a.m = 20;
    return a;
}

fn(a);
console.log(a);   // { m: 20, n: 20 }

正是由于這樣的不同,導(dǎo)致了許多人在理解函數(shù)參數(shù)的傳遞方式時,就有許多困惑。到底是按值傳遞還是按引用傳遞?實(shí)際上結(jié)論仍然是按值傳遞,只不過當(dāng)我們期望傳遞一個引用類型時,真正傳遞的,只是這個引用類型保存在變量對象中的引用而已。為了說明這個問題,我們看看下面這個例子。

var person = {
    name: "Nicholas",
    age: 20
}

function setName(obj) {  // 傳入一個引用
    obj = {};   // 將傳入的引用指向另外的值
    obj.name = "Greg";  // 修改引用的name屬性
}

setName(person);
console.log(person.name);  // Nicholas 未被改變

在上面的例子中,如果person是按引用傳遞,那么person就會自動被修改為指向其name屬性值為Gerg的新對象。但是我們從結(jié)果中看到,person對象并未發(fā)生任何改變,因此只是在函數(shù)內(nèi)部引用被修改而已。

四、函數(shù)式編程

雖然JavaScript并不是一門純函數(shù)式編程的語言,但是它使用了許多函數(shù)式編程的特性。因此了解這些特性可以讓我們更加了解自己寫的代碼。

當(dāng)我們想要使用一個函數(shù)時,通常情況下其實(shí)就是想要將一些功能,邏輯等封裝起來。相信大家對于封裝這個概念并不陌生。

我們通常通過函數(shù)封裝來完成一件事情。例如,我想要計(jì)算任意三個數(shù)的和,我們就可以將這三個數(shù)作為參數(shù),封裝一個簡單的函數(shù)。

function add(a, b, c) {
  return a + b + c;
}

當(dāng)我們想要計(jì)算三個數(shù)的和時,直接調(diào)用該方法即可。

add(1, 2, 3); // 6

當(dāng)然,當(dāng)我們想要做的事情比較簡單的時候,可能還看不出來封裝成為函數(shù)之后帶來的便利。如果我們想要做的事情稍微復(fù)雜一點(diǎn)呢。例如我想要計(jì)算一個數(shù)組中的所有子項(xiàng)目的和。

function mergeArr(arr) {
    var result = 0;
    for(var i = 0; i < arr.length; i++) { result  += arr[i] }
    return result;
}

如果我們不通過函數(shù)封裝的方式,那么再每次想要實(shí)現(xiàn)這個功能時,就不得不重新使用一次for循環(huán),這樣的后果就是我們的代碼中充斥著越來越多的重復(fù)代碼。而封裝之后,當(dāng)我們想要再次做這件事情的時候,只需要一句話就可以了。

mergeArr([1, 2, 3, 4, 5]);

當(dāng)然,我相信大家對于函數(shù)封裝的意義都應(yīng)該有非常明確的認(rèn)知,但是我們要面臨的問題是,當(dāng)我們想要去封裝一個函數(shù)時,如何做才是最佳實(shí)踐呢?

函數(shù)式編程能給我們答案。

我們在初學(xué)時,往往會不由自主的使用命令式編程的風(fēng)格來完成我們想要干的事情。因?yàn)槊钍骄幊谈拥暮唵危卑住@缥覀儸F(xiàn)在有一個數(shù)組,array = [1, 3, "h", 5, "m", "4"],現(xiàn)在想要找出這個數(shù)組中的所有類型為number的子項(xiàng)。當(dāng)我們使用命令式編程思維時,可能就會直接這樣做。

var array = [1, 3, "h", 5, "m", "4"];
var res = [];
for(var i = 0; i < array.length; i ++) {
    if (typeof array[i] === "number") {
        res.push(array[i]);
    }
}

在這種實(shí)現(xiàn)方式中,我們平鋪直敘的實(shí)現(xiàn)了我們的目的。這樣做的問題在于,當(dāng)我們在另外的時刻,想要找出另外一個數(shù)組中所有的子項(xiàng)時,我們不得不把同樣的邏輯再寫一次。當(dāng)出現(xiàn)次數(shù)變多時,我們的代碼也變得更加糟糕且難以維護(hù)。

而函數(shù)式編程的思維則建議我們將這種會多次出現(xiàn)的功能封裝起來以備調(diào)用。

function getNumbers(array) {
    var res = [];
    array.forEach(function(item) {
        if (typeof item === "number") {
            res.push(item);
        }
    })
    return res;
}



// 以上是我們的封裝,以下是功能實(shí)現(xiàn)
var array = [1, 3, "h", 5, "m", "4"];
var res = getNumbers(array);

因此當(dāng)我們將功能封裝之后,我們實(shí)現(xiàn)同樣的功能時,只需要寫一行代碼。而如果未來需求變動,或者稍作修改,我們只需要對getNumbers方法進(jìn)行調(diào)整就可以了。而且我們在使用時,只需要關(guān)心這個方法能做什么,而不用關(guān)心他具體是怎么實(shí)現(xiàn)的。這也是函數(shù)式編程思維與命令式不同的地方之一。

函數(shù)式編程思維還具有以下幾個特征。

函數(shù)是第一等公民

所謂"第一等公民"(first class),指的是函數(shù)與其他數(shù)據(jù)類型一樣,處于平等地位,可以賦值給其他變量,也可以作為參數(shù),傳入另一個函數(shù),或者作為別的函數(shù)的返回值。這些場景,我們應(yīng)該見過很多。

var a = function foo() {}  // 賦值
function fn(function() {}, num) {}   // 函數(shù)作為參數(shù)

// 函數(shù)作為返回值
function var() {
    return function() {
        ... ...
    }
}

當(dāng)然,這都是JavaScript的基本概念。但是我想很多人,甚至包括正在閱讀的你自己都可能會無視這些概念。可以用一個簡單的例子來驗(yàn)證一下。

我們先自定義這樣一個函數(shù)。

function delay() {
    console.log("5000ms之后執(zhí)行該方法.");
}

現(xiàn)在要做的是,如果要求你結(jié)合setTimeout方法,讓delay方法延遲5000ms執(zhí)行,應(yīng)該怎么做?

其實(shí)很簡單,對不對,直接這樣就可以了。

var timer = setTimeout(function() {
    delay();
}, 5000);

那么現(xiàn)在問題來了,如果你對函數(shù)是一等公民有一個深刻的認(rèn)知,我想你會發(fā)現(xiàn)上面這種寫法其實(shí)是有一些問題的。所以思考一下,問題出在哪里?

函數(shù)既然能夠作為一個參數(shù)傳入另外一個函數(shù),那么我們是不是可以直接將delay作為setTimeout的第一個參數(shù),而不用額外的多加一層匿名函數(shù)呢?

因此,其實(shí)最正確的解法應(yīng)該這樣寫。

var timer = setTimeout(delay, 5000);

當(dāng)然,如果你已經(jīng)提前想到這樣做了,那么恭喜你,說明你在JavaScript上比普通人更有天賦。其實(shí)第一種糟糕的方式很多人都在用,包括有多年工作經(jīng)驗(yàn)的人也沒有完全避免。而他們甚至還不知道自己問題出在什么地方。

在未來的實(shí)踐中,你還會遇到更多類似的場景。為了驗(yàn)證讀者朋友們的理解,我們不妨來思考一下如何優(yōu)化下面的代碼。

function getUser(path, callback) {
    return $.get(path, function(info) {
        return callback(info);
    })
}

getUser("/api/user", function(resp) {
    // resp為成功請求之后返回的數(shù)據(jù)
    console.log(resp);
})

優(yōu)化的原理和setTimeout的例子一模一樣,我這里賣個關(guān)子,不打算告訴大家結(jié)論,僅提示一句,getUser優(yōu)化之后,僅有一句代碼。考驗(yàn)大家學(xué)習(xí)成果的時候到了 ^ ^。

只用"表達(dá)式",不用"語句"

"表達(dá)式"(expression)是一個單純的運(yùn)算過程,總是有返回值;"語句"(statement)是執(zhí)行某種操作,沒有返回值。函數(shù)式編程要求,只使用表達(dá)式,不使用語句。也就是說,每一步都是單純的運(yùn)算,而且都有返回值。

假如我們的項(xiàng)目中,多處需要改變某個元素的背景色。因此我們可以這樣封裝一下。

var ele = document.querySelector(".test");
function setBackgroundColor(color) {
    ele.style.backgroundColor = color;
}

// 多處使用
setBackgroundColor("red");
setBackgroundColor("#ccc");

我們可以很明顯的感受到,setBackgroundColor封裝的僅僅只是一條語句。這并不是理想的效果。函數(shù)式編程期望一個函數(shù)有輸入,也有輸出。因此良好的習(xí)慣應(yīng)該如下做。

function setBackgroundColor(ele, color) {
    ele.style.backgroundColor = color;
    return color;
}

// 多處使用
var ele = document.querySelector(".test");
setBackgroundColor(ele, "red");
setBackgroundColor(ele, "#ccc");

了解這一點(diǎn),可以讓我們自己在封裝函數(shù)的時候養(yǎng)成良好的習(xí)慣。

純函數(shù)

相同的輸入總會得到相同的輸出,并且不會產(chǎn)生副作用的函數(shù),就是純函數(shù)。

所謂"副作用"(side effect),指的是函數(shù)內(nèi)部與外部互動(最典型的情況,就是修改全局變量的值),產(chǎn)生運(yùn)算以外的其他結(jié)果。

函數(shù)式編程強(qiáng)調(diào)沒有"副作用",意味著函數(shù)要保持獨(dú)立,所有功能就是返回一個新的值,沒有其他行為,尤其是不得修改外部變量的值。

即所謂的只要是同樣的參數(shù)傳入,返回的結(jié)果一定是相等的。

例如我們期望封裝一個函數(shù),能夠得到傳入數(shù)組的最后一項(xiàng)。那么可以通過下面兩種方式來實(shí)現(xiàn)。

function getLast(arr) {
    return arr[arr.length];
}

function getLast_(arr) {
    return arr.pop();
}

var source = [1, 2, 3, 4];

var last = getLast(source); // 返回結(jié)果4 原數(shù)組不變
var last_ = getLast_(source); // 返回結(jié)果4 原數(shù)據(jù)最后一項(xiàng)被刪除

getLast與getLast_雖然同樣能夠獲得數(shù)組的最后一項(xiàng)值,但是getLast_改變了原數(shù)組。而當(dāng)原始數(shù)組被改變,那么當(dāng)我們再次調(diào)用該方法時,得到的結(jié)果就會變得不一樣。這樣不可預(yù)測的封裝方式,在我們看來是非常糟糕的。它會把我們的數(shù)據(jù)搞得非常混亂。在JavaScript原生支持的數(shù)據(jù)方法中,也有許多不純的方法,我們在使用時需要非常警惕,我們要清晰的知道原始數(shù)據(jù)的改變是否會留下隱患。

var source = [1, 2, 3, 4, 5];

source.slice(1, 3); // 純函數(shù) 返回[2, 3] source不變
source.splice(1, 3); // 不純的 返回[2, 3, 4] source被改變

source.pop(); // 不純的
source.push(6); // 不純的
source.shift(); // 不純的
source.unshift(1); // 不純的
source.reverse(); // 不純的

// 我也不能短時間知道現(xiàn)在source被改變成了什么樣子,干脆重新約定一下
source = [1, 2, 3, 4, 5];

source.concat([6, 7]); // 純函數(shù) 返回[1, 2, 3, 4, 5, 6, 7] source不變
source.join("-"); // 純函數(shù) 返回1-2-3-4-5 source不變

閉包

閉包是函數(shù)式編程語言的重要特性,我也在前面幾篇文章中說了很多關(guān)于閉包的內(nèi)容。這里不再贅述。

柯里化

下一章。

前端基礎(chǔ)進(jìn)階系列目錄

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/90548.html

相關(guān)文章

  • 文章分享(持續(xù)更新)

    摘要:文章分享持續(xù)更新更多資源請文章轉(zhuǎn)自一前端文章基礎(chǔ)篇,,前端基礎(chǔ)進(jìn)階一內(nèi)存空間詳細(xì)圖解前端基礎(chǔ)進(jìn)階二執(zhí)行上下文詳細(xì)圖解前端基礎(chǔ)進(jìn)階三變量對象詳解前端基礎(chǔ)進(jìn)階四詳細(xì)圖解作用域鏈與閉包前端基礎(chǔ)進(jìn)階五全方位解讀前端基礎(chǔ)進(jìn)階六在開發(fā)者工具中觀察函數(shù)調(diào) 文章分享(持續(xù)更新) 更多資源請Star:https://github.com/maidishike... 文章轉(zhuǎn)自:https://gith...

    whlong 評論0 收藏0
  • 前端基礎(chǔ)進(jìn)階目錄

    摘要:不過其實(shí)簡書文章評論里有很多大家的問題以及解答,對于進(jìn)一步理解文中知識幫助很大的,算是有點(diǎn)可惜吧。不過也希望能夠?qū)φ趯W(xué)習(xí)前端的你有一些小幫助。如果在閱讀中發(fā)現(xiàn)了一些錯誤,請?jiān)谠u論里告訴我,我會及時更改。 前端基礎(chǔ)進(jìn)階(一):內(nèi)存空間詳細(xì)圖解 前端基礎(chǔ)進(jìn)階(二):執(zhí)行上下文詳細(xì)圖解 前端基礎(chǔ)進(jìn)階(三):變量對象詳解 前端基礎(chǔ)進(jìn)階(四):詳細(xì)圖解作用域鏈與閉包 前端基礎(chǔ)進(jìn)階(五):全方位...

    mo0n1andin 評論0 收藏0
  • 前端2018現(xiàn)在上車還還得及么

    摘要:面向?qū)ο笕筇卣骼^承性多態(tài)性封裝性接口。第五階段封裝一個屬于自己的框架框架封裝基礎(chǔ)事件流冒泡捕獲事件對象事件框架選擇框架。核心模塊和對象全局對象,,,事件驅(qū)動,事件發(fā)射器加密解密,路徑操作,序列化和反序列化文件流操作服務(wù)端與客戶端。 第一階段: HTML+CSS:HTML進(jìn)階、CSS進(jìn)階、div+css布局、HTML+css整站開發(fā)、 JavaScript基礎(chǔ):Js基礎(chǔ)教程、js內(nèi)置對...

    stormgens 評論0 收藏0
  • 前端2018現(xiàn)在上車還還得及么

    摘要:面向?qū)ο笕筇卣骼^承性多態(tài)性封裝性接口。第五階段封裝一個屬于自己的框架框架封裝基礎(chǔ)事件流冒泡捕獲事件對象事件框架選擇框架。核心模塊和對象全局對象,,,事件驅(qū)動,事件發(fā)射器加密解密,路徑操作,序列化和反序列化文件流操作服務(wù)端與客戶端。 第一階段: HTML+CSS:HTML進(jìn)階、CSS進(jìn)階、div+css布局、HTML+css整站開發(fā)、 JavaScript基礎(chǔ):Js基礎(chǔ)教程、js內(nèi)置對...

    mylxsw 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<