JavaScript 精粹 基礎(chǔ) 進(jìn)階(7)函數(shù)和作用域(閉包、作用域)

閉包在JavaScript 中是一個(gè)非常重要的概念厕怜。

閉包例子

function outer() {
    var loc = 30;
    return loc;
};
console.log(outer()); //30

outer函數(shù)是一個(gè)函數(shù)聲明衩匣,有一個(gè)局部變量loc賦值為30,返回loc粥航。

當(dāng)這個(gè)函數(shù)調(diào)用之后琅捏,局部變量就會(huì)被釋放了,

function outer() {
    var loc = 30;
    return function() {
        return loc;
    };
};
var func = outer();
console.log(func()); //30

這個(gè)outer函數(shù)有一個(gè)loc的局部變量递雀,返回值是一個(gè)匿名函數(shù)表達(dá)式柄延,在這個(gè)函數(shù)表達(dá)式又返回outer函數(shù)的loc局部變量,這種情況loc是不會(huì)被釋放掉的缀程。

調(diào)用var func = outer();返回的是一個(gè)匿名函數(shù)搜吧,這個(gè)匿名函數(shù)里面仍然能夠訪問到outer()de 局部變量loc,當(dāng)outer()函數(shù)被調(diào)用之后杠输,func()這個(gè)函數(shù)再次調(diào)用的時(shí)候任然能訪問到它外層outer()函數(shù)的局部變量loc赎败。
這種情況就是我們通常所說的閉包

那么閉包的作用是什么呢蠢甲?在前端編程里僵刮,閉包是非常常見的

閉包無處不在

<body>
<input type="button" value="按鈕" id="but">
<script type="text/javascript">
var but = document.getElementById("but");
! function() {
    var loc = 0;
    but.onclick = function() {
        console.log(loc++);
    };
}();
</script>
</body>
Paste_Image.png
<input type="button" value="按鈕" id="but">
<script type="text/javascript">
var but = document.getElementById("but");
! function() {
    var loc = "locc";
    but.addEventListener('click', function() {
        console.log(loc);
    }, false);
}();
</script>

比如說我們可能有一些自己的局部變量,或者說我們的函數(shù)有一些外函數(shù)的變量鹦牛,我們?cè)谧鳇c(diǎn)擊事件的時(shí)候搞糕,這個(gè)點(diǎn)擊事件可能會(huì)用到外層的一些局部變量,有了閉包我們可以在數(shù)據(jù)的傳遞上更為靈活曼追。

! function() {
    var loc = "loc";
    var url = "http://www.huanghanlian.com/";
    $.ajax({
        url: url,
        success: function() {
            //do sth
            console.log(loc);
        }
    });
}();

有一個(gè)異步的請(qǐng)求窍仰,用jq的$.ajax方法也可以在success的回調(diào)函數(shù)中去用到外層具體的一些變量。

因?yàn)殚]包的緣故礼殊,在最外層匿名函數(shù)調(diào)用結(jié)束后驹吮,success的回調(diào)函數(shù)仍然可以訪問外層的局部變量。loc晶伦,url

閉包-常見錯(cuò)誤之循環(huán)閉包

document.body.innerHTML = "<div id=a1>aa</div>" + "<div id=a2>bb</div>" + "<div id=a3>cc</div>" + "<div id=a4>dd</div>";
for (var i = 1; i < 4; i++) {
    document.getElementById("a" + i).addEventListener('click', function() {
        console.log(i);//始終都是4
    }, false);
};

這里面在網(wǎng)頁上面添加三個(gè)元素碟狞,通過循環(huán)來給這三個(gè)元素綁定事件,想當(dāng)點(diǎn)擊第一個(gè)元素的時(shí)候婚陪,輸出1點(diǎn)擊第二個(gè)輸出2第三個(gè)輸出3族沃。實(shí)現(xiàn)方式是先循環(huán)獲取元素,給元素增加點(diǎn)擊事件,點(diǎn)擊事件里面會(huì)輸出i的值脆淹。

但是上面的代碼始終只會(huì)輸出4常空,這其實(shí)是閉包的原因,addEventListener('click', function() {這里是回調(diào)函數(shù)}是回調(diào)函數(shù)盖溺,也就是說當(dāng)點(diǎn)擊時(shí)回調(diào)函數(shù)去做你想做的事情漓糙, 當(dāng)我點(diǎn)擊的濕乎乎回調(diào)函數(shù)回去動(dòng)態(tài)的拿到 i的值,這個(gè)i的值在整個(gè)初始化過程中完成之后實(shí)際上i的值就已經(jīng)是4了咐柜,所以始終輸出4兼蜈。

如果想想要實(shí)現(xiàn)效果就要用到閉包

document.body.innerHTML = "<div id=a1>aa</div>" + "<div id=a2>bb</div>" + "<div id=a3>cc</div>";
for (var i = 1; i < 4; i++) {
    ! function(i) {
        document.getElementById("a" + i).addEventListener('click', function() {
            console.log(i);//1,2,3
        }, false);
    }(i);
};

這個(gè)例子里面在每一層循環(huán)的時(shí)候,用一個(gè)匿名函數(shù)而且是立即執(zhí)行的匿名函數(shù)給他包裝起來拙友,然后將每一次遍歷的1.2.3分別的值去傳到這個(gè)匿名函數(shù)里为狸,然后匿名函數(shù)接到這個(gè)參數(shù)i再放到點(diǎn)擊事件中去引用i當(dāng)我們每次點(diǎn)擊事件輸出的值i就會(huì)取每一個(gè)閉包環(huán)境下的i。所以這樣就能達(dá)到效果遗契。

閉包-封裝

閉包還有個(gè)好處就是可以封裝一些變量

(function() {
    var _userId = 23492;
    var _typeId = 'item';
    var expor = {};

    function converter(userId) {
        return +userId;
    };

    expor.getUserId = function() {
        return converter(_userId);
    };

    expor.getTypeId = function() {
        return _typeId;
    };
    window.expor = expor;
}());
console.log(expor.getUserId()); //23492
console.log(expor.getTypeId()); //item
console.log(expor._userId); //undefined
console.log(expor._typeId); //undefined

比如說以上代碼辐棒,有個(gè)立即調(diào)用函數(shù),他有自己的函數(shù)作用域牍蜂,在里面定義的局部變量外部是不可以訪問到的漾根,最后可以通過window.expor = expor;這樣的方式來去把最終想輸出的對(duì)象輸出出去,那么外部就可以通過expor對(duì)象上提供的方法鲫竞,就可以訪問到函數(shù)里面的對(duì)象或變量辐怕。

閉包的概念

在計(jì)算機(jī)科學(xué)中,閉包(也稱詞法閉包或函數(shù)閉包)是指一個(gè)函數(shù)或函數(shù)的引用从绘,與一個(gè)引用環(huán)境綁在一起寄疏。這個(gè)引用環(huán)境是一個(gè)存儲(chǔ)該函數(shù)每個(gè)非局部變量(也叫自由變量)的表。

閉包僵井,不同于一般函數(shù)陕截,它允許一個(gè)函數(shù)在立即詞法作用域外調(diào)用時(shí),仍可訪問非本地變量

作用域

全局批什,函數(shù)农曲,eval [作用域]

JavaScript中的作用域,實(shí)際上是比較簡(jiǎn)單的驻债。

全局作用域

var a = 10;
console.log(window.a); //10

在最外層聲明變量a乳规,這樣就聲明了全局作用域下的變量a。

for (var i = 0; i < 4; i++) {
    console.log(i); //0.1.2.3
}
console.log(i); //4

在全局范圍內(nèi)用for循環(huán)里面有個(gè)vae i=0定義了一個(gè)變量i合呐,可能會(huì)誤解為這個(gè)i只能在這個(gè)for循環(huán)里面可見驯妄,對(duì)外不可見,實(shí)際上這是錯(cuò)誤的理解合砂,JavaScript中是沒有塊級(jí)作用域的,也就是說不管是for循環(huán)while 循環(huán)里面去定義的變量,實(shí)際上和在外面定義的變量翩伪,也就是for循環(huán)所在的外面去定義變量實(shí)際上是沒有差別的微猖,所以 i在外面也能訪問到。

函數(shù)作用域

(function() {
    var b = 20;
})();
console.log(b);//test_lt.html:13 Uncaught ReferenceError: b is not defined(…)

上面匿名立即執(zhí)行函數(shù)表達(dá)式缘屹,在執(zhí)行的時(shí)候聲明局部變量b凛剥,在函數(shù)外面是拿不到的。函數(shù)有自己獨(dú)立的作用域轻姿。

eval 作用域

eval("var a=1;");

作用域鏈

var le3 = 3;

function out() {
    var le = 1;

    function out2() {
        var le2 = 2;
        console.log(le, le2, le3); //1 2 3
    }
    out2()
}
out();

out()函數(shù)中有一個(gè)內(nèi)部函數(shù)out2()它里面有一個(gè)局部變量le2犁珠,函數(shù)out2()能訪問到自己的內(nèi)部變量le2,也可以在向上訪問le變量互亮,也就是所謂的閉包犁享,可以訪問外層函數(shù)的局部變量,對(duì)于函數(shù)out2()來講也叫作它的自由變量豹休,還可以訪問最外層的le3變量炊昆,也就是全局對(duì)象,這個(gè)作用域手機(jī)從內(nèi)向外都可以訪問的威根。

function out() {
    var le = 1;
    var func = new Function("var p=0;console.log(p);console.log(le)");
    func();
}
out();
//輸出
//0
//Uncaught ReferenceError: le is not defined(…)

如果使用new Function去構(gòu)造的一個(gè)函數(shù)凤巨,構(gòu)造器,去調(diào)用構(gòu)造器定義位置所在的變量le的洛搀。

利用函數(shù)作用域封裝

(function() {
    //do sth here
    var a, b;
})();

! function() {
    //do sth here
    var a, b;
}();

利用函數(shù)作用域的特性敢茁,我們經(jīng)常看到很多類庫留美,或者代碼最外層如果沒有模塊化的一些工具的話彰檬,會(huì)在最外層去寫一個(gè)function這樣一個(gè)匿名函數(shù),這樣的好處就是可以把函數(shù)內(nèi)部的變量變成函數(shù)的局部變量独榴,而不是全局變量僧叉,這樣防止大量的全局變量和其他的一些類庫或者其他代碼引發(fā)沖突,! function() {}()這樣的作用其實(shí)就是把函數(shù)變成函數(shù)表達(dá)式棺榔,而不是函數(shù)聲明瓶堕,如果省略掉這個(gè)!嘆號(hào)的話,那么他會(huì)理解為函數(shù)聲明症歇,會(huì)被前置處理掉郎笆,那么最后留下一個(gè)括號(hào)或者你省略名字的話,都會(huì)報(bào)語法錯(cuò)誤忘晤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宛蚓,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子设塔,更是在濱河造成了極大的恐慌凄吏,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異痕钢,居然都是意外死亡图柏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門任连,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚤吹,“玉大人,你說我怎么就攤上這事随抠〔米牛” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵拱她,是天一觀的道長(zhǎng)二驰。 經(jīng)常有香客問我,道長(zhǎng)椭懊,這世上最難降的妖魔是什么诸蚕? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮氧猬,結(jié)果婚禮上背犯,老公的妹妹穿的比我還像新娘。我一直安慰自己盅抚,他們只是感情好漠魏,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著妄均,像睡著了一般柱锹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上丰包,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天禁熏,我揣著相機(jī)與錄音,去河邊找鬼邑彪。 笑死瞧毙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的寄症。 我是一名探鬼主播宙彪,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼有巧!你這毒婦竟也來了释漆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤篮迎,失蹤者是張志新(化名)和其女友劉穎男图,沒想到半個(gè)月后示姿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡享言,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年峻凫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片览露。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖譬胎,靈堂內(nèi)的尸體忽然破棺而出差牛,到底是詐尸還是另有隱情,我是刑警寧澤堰乔,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布偏化,位于F島的核電站,受9級(jí)特大地震影響镐侯,放射性物質(zhì)發(fā)生泄漏侦讨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一苟翻、第九天 我趴在偏房一處隱蔽的房頂上張望韵卤。 院中可真熱鬧,春花似錦崇猫、人聲如沸沈条。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜡歹。三九已至,卻和暖如春涕烧,著一層夾襖步出監(jiān)牢的瞬間月而,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工议纯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留父款,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓痹扇,卻偏偏與公主長(zhǎng)得像铛漓,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鲫构,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容