久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

前端基礎進階(7):函數(shù)與函數(shù)式編程

 Earlycl6i7o9ek 2020-02-18

作者:波同學 

www.jianshu.com/p/69dede6f7e5f

縱觀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ù)。

fn();  // function

function fn() {

    console.log('function');

}

函數(shù)表達式

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

// 變量聲明

var a = 20;

// 實際執(zhí)行順序

var a = undefined;  // 變量聲明,,初始值undefined,,變量提升,提升順序次于function聲明

a = 20;  // 變量賦值,,該操作不會提升

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

fn(); // 報錯

var fn = function() {

    console.log('function');

}

上例子的執(zhí)行順序為:

var fn = undefined;   // 變量聲明提升

fn();    // 執(zhí)行報錯

fn = function() {   // 賦值操作,此時將后邊函數(shù)的引用賦值給fn

    console.log('function');

}

因此,,由于聲明方式的不同,,導致了函數(shù)聲明與函數(shù)表達式在使用上的一些差異需要我們注意,除此之外,,這兩種形式的函數(shù)在使用上并無不同,。

關(guān)于上面例子中,函數(shù)表達式中的賦值操作,,在其他一些地方也會被經(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ù)表達式中的賦值操作,。而匿名函數(shù),,顧名思義,就是指的沒有被顯示進行賦值操作的函數(shù),。它的使用場景,,多作為一個參數(shù)傳入另一個函數(shù)中。

var a = 10;

var fn = function(bar, num) {

    return bar()   num;

}

fn(function() {

    return a;

}, 20)

在上面的例子中,,fn的第一個參數(shù)傳入了一個匿名函數(shù),。雖然該匿名函數(shù)沒有顯示的進行賦值操作,,我們沒有辦法在外部執(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)榛顒訉ο?,并完成賦值操作與執(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)容,,我會在下一篇深入探討柯里化的文章中進行更加詳細講解。

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

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

在ES5中,,沒有塊級作用域,,因此我們常常使用函數(shù)自執(zhí)行的方式來模仿塊級作用域,這樣就提供了一個獨立的執(zhí)行上下文,,結(jié)合閉包,,就為模塊化提供了基礎。

(function() {

   // ...

})();

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

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

(function() {

    // 私有變量

    var age = 20;

    var name = 'Tom';

    // 私有方法

    function getName() {

        return `your name is `   name;

    }

})();

但是共有方法和變量應該怎么辦?大家還記得我們前面講到過的閉包的特性嗎,?沒錯,,利用閉包,我們可以訪問到執(zhí)行上下文內(nèi)部的變量和方法,,因此,,我們只需要根據(jù)閉包的定義,創(chuà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;

})();

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

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

(function(window, undefined) {

    // 聲明jQuery構(gòu)造函數(shù)

     var jQuery = function(name) {

        // 主動在構(gòu)造函數(shù)中,,返回一個jQuery實例

         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ù),因為在jQuery的構(gòu)造函數(shù)中通過一些手段,,返回的是jQuery的實例,,所以我們就不用再每次用的時候在自己new了

$('#div1');

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

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

先來直接看代碼,。

// 自執(zhí)行創(chuàng)建模塊

(function() {

    // states 結(jié)構(gòu)預覽

    // 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ù)實現(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實例在線地址: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)存中的值。當改變其中一個時,,另外一個自然也被改變,。如下例。

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

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

var a = 20;

function fn(a) {

    a = a 10;

    return 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 }

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

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ù)式編程的特性,。因此了解這些特性可以讓我們更加了解自己寫的代碼。

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

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

var a = function foo() {}  // 賦值

function fn(function() {}, num) {}   // 函數(shù)作為參數(shù)

// 函數(shù)作為返回值

function var() {

    return function() {

        ... ...

    }

}

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

“表達式”(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ù)中我們常常會利用模塊中的私有變量等,。

普通封裝

function add(num1, num2) {

    return num1 num2;

}

add(20, 10); // 30

掛載在對象上

if(typeof Array.prototype.add !== 'function') {

  Array.prototype.add = function() {

    var i = 0,

        len = this.length,

        result = 0;

    for( ; i < len; i ) {

        result = this[i]

    }

    return result;

  }

}

[1, 2, 3, 4].add() // 10

修改數(shù)組對象的例子,常在面試中被問到類似的,,但是并不建議在實際開發(fā)中擴展原生對象,。與普通封裝不一樣的是,因為掛載在對象的原型上我們可以通過this來訪問對象的屬性和方法,,所以這種封裝在實際使用時會有許多的難點,,因此我們一定要掌握好this,。

本系列:

    本站是提供個人知識管理的網(wǎng)絡存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導購買等信息,,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多