日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

javascript中的闭包closure详解

發(fā)布時(shí)間:2024/2/28 javascript 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 javascript中的闭包closure详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 簡(jiǎn)介
  • 函數(shù)中的函數(shù)
  • Closure閉包
  • 使用閉包實(shí)現(xiàn)private方法
  • 閉包的Scope Chain
  • 閉包常見的問題
  • 閉包性能的問題
  • 總結(jié)

簡(jiǎn)介

閉包c(diǎn)losure是javascript中一個(gè)非常強(qiáng)大的功能。所謂閉包就是函數(shù)中的函數(shù),內(nèi)部函數(shù)可以訪問外部函數(shù)的作用域范圍,從而可以使用閉包來做一些比較強(qiáng)大的工作。

今天將會(huì)給大家詳細(xì)介紹一下閉包。

函數(shù)中的函數(shù)

我們提到了函數(shù)中的函數(shù)可以訪問父函數(shù)作用域范圍的變量,我們看一個(gè)例子:

function parentFunction() {var address = 'flydean.com'; function alertAddress() { alert(address); }alertAddress(); } parentFunction();

上面的例子中,我們?cè)趐arentFunction中定義了一個(gè)變量address,在parentFunction內(nèi)部定義了一個(gè)alertAddress方法,在該方法內(nèi)部訪問外部函數(shù)中定義的address變量。

上面代碼運(yùn)行是沒問題的,可以正確的訪問到數(shù)據(jù)。

Closure閉包

函數(shù)中的函數(shù)有了,那么什么是閉包呢?

我們看下面的例子:

function parentFunction() {var address = 'flydean.com'; function alertAddress() { alert(address); }return alertAddress; } var myFunc = parentFunction(); myFunc();

這個(gè)例子和第一個(gè)例子很類似,不同之處就是我們將內(nèi)部函數(shù)返回了,并且賦值給了myFunc。

接下來我們直接調(diào)用了myFunc。

myFunc中訪問了parentFunction中的address變量,雖然parentFunction已經(jīng)執(zhí)行完畢返回。

但是我們?cè)谡{(diào)用myFunc的時(shí)候,任然可以訪問到address變量。這就是閉包。

閉包的這個(gè)特性非常擁有,我們可以使用閉包來生成function factory,如下所示:

function makeAdder(x) {return function(y) {return x + y;}; }var add5 = makeAdder(5); var add10 = makeAdder(10);console.log(add5(2)); // 7 console.log(add10(2)); // 12

其中add5和add10都是閉包,他們是由makeAdder這個(gè)function factory創(chuàng)建出來的。通過傳遞不同的x參數(shù),我們得到了不同的基數(shù)的add方法。

最終生成了兩個(gè)不同的add方法。

使用function factory的概念,我們可以考慮一個(gè)閉包的實(shí)際應(yīng)用,比如我們?cè)陧撁嫔嫌腥齻€(gè)button,通過點(diǎn)擊這些button可實(shí)現(xiàn)修改字體的功能。

我們可以先通過function factory來生成三個(gè)方法:

function makeSizer(size) {return function() {document.body.style.fontSize = size + 'px';}; }var size12 = makeSizer(12); var size14 = makeSizer(14); var size16 = makeSizer(16);

有了這三個(gè)方法,我們把DOM元素和callback方法綁定起來:

document.getElementById('size-12').onclick = size12; document.getElementById('size-14').onclick = size14; document.getElementById('size-16').onclick = size16;

使用閉包實(shí)現(xiàn)private方法

對(duì)比java來說,java中有private訪問描述符,通過private,我們可以指定方法只在class內(nèi)部訪問。

當(dāng)然,在JS中并沒有這個(gè)東西,但是我們可以使用閉包來達(dá)到同樣的效果。

var counter = (function() {var privateCounter = 0;function changeBy(val) {privateCounter += val;}return {increment: function() {changeBy(1);},decrement: function() {changeBy(-1);},value: function() {return privateCounter;}}; })();console.log(counter.value()); // 0.counter.increment(); counter.increment(); console.log(counter.value()); // 2.counter.decrement(); console.log(counter.value()); // 1.

我們?cè)诟竑unction中定義了privateCounter屬性和changeBy方法,但是這些方法只能夠在內(nèi)部function中訪問。

我們通過閉包的概念,將這些屬性和方法封裝起來,暴露給外部使用,最終達(dá)到了私有變量和方法封裝的效果。

閉包的Scope Chain

對(duì)于每個(gè)閉包來說,都有一個(gè)作用域范圍,包括函數(shù)本身的作用域,父函數(shù)的作用域和全局的作用域。

如果我們?cè)诤瘮?shù)內(nèi)部嵌入了新的函數(shù),那么就會(huì)形成一個(gè)作用域鏈,我們叫做scope chain。

看下面的一個(gè)例子:

// global scope var e = 10; function sum(a){return function(b){return function(c){// outer functions scopereturn function(d){// local scopereturn a + b + c + d + e;}}} }console.log(sum(1)(2)(3)(4)); // log 20

閉包常見的問題

第一個(gè)常見的問題就是在循環(huán)遍歷中使用閉包,我們看一個(gè)例子:

function showHelp(help) {document.getElementById('help').innerHTML = help; }function setupHelp() {var helpText = [{'id': 'email', 'help': 'Your e-mail address'},{'id': 'name', 'help': 'Your full name'},{'id': 'age', 'help': 'Your age (you must be over 16)'}];for (var i = 0; i < helpText.length; i++) {var item = helpText[i];document.getElementById(item.id).onfocus = function() {showHelp(item.help);}} }setupHelp();

上面的例子中,我們創(chuàng)建了一個(gè)setupHelp函數(shù),setupHelp中,onfocus方法被賦予了一個(gè)閉包,所以閉包中的item可以訪問到外部function中定義的item變量。

因?yàn)樵谘h(huán)里面賦值,所以我們實(shí)際上創(chuàng)建了3個(gè)閉包,但是這3個(gè)閉包共享的是同一個(gè)外部函數(shù)的作用域范圍。

我們的本意是,不同的id觸發(fā)不同的help消息。但是如果我們真正執(zhí)行就會(huì)發(fā)現(xiàn),不管是哪一個(gè)id,最終的消息都是最后一個(gè)。

因?yàn)閛nfocus是在閉包創(chuàng)建完畢之后才會(huì)觸發(fā),這個(gè)時(shí)候item的值實(shí)際上是變化的,在循環(huán)結(jié)束之后,item的值已經(jīng)指向了最后一個(gè)元素,所以全部顯示的是最后一條數(shù)據(jù)的help消息。

怎么解決這個(gè)問題呢?

最簡(jiǎn)單的辦法使用ES6中引入的let描述符,從而將item定義為block的作用域范圍,每次循環(huán)都會(huì)創(chuàng)建一個(gè)新的item,從而保持閉包中的item的值不變。

for (let i = 0; i < helpText.length; i++) {let item = helpText[i];document.getElementById(item.id).onfocus = function() {showHelp(item.help);}}

還有一種方法,就是再創(chuàng)建一個(gè)閉包:

function makeHelpCallback(help) {return function() {showHelp(help);}; }for (var i = 0; i < helpText.length; i++) {var item = helpText[i];document.getElementById(item.id).onfocus = makeHelpCallback(item.help);}

這里用到了之前我們提到的function factory的概念,我們?yōu)椴煌拈]包創(chuàng)建了不同的作用域環(huán)境。

還有一種方法就是將item包含在一個(gè)新的function作用域范圍之內(nèi),從而每次創(chuàng)建都是新的item,這個(gè)和let的原理是相似的:

for (var i = 0; i < helpText.length; i++) {(function() {var item = helpText[i];document.getElementById(item.id).onfocus = function() {showHelp(item.help);}})(); }

第二個(gè)常見的問題就是內(nèi)存泄露。

function parentFunction(paramA){var a = paramA;function childFunction(){return a + 2;}return childFunction();}

上面的例子中,childFunction引用了parentFunction的變量a。只要childFunction還在被使用,a就無法被釋放,從而導(dǎo)致parentFunction無法被垃圾回收。

閉包性能的問題

我們定義了一個(gè)對(duì)象,并且通過閉包來訪問其私有屬性:

function MyObject(name, message) {this.name = name.toString();this.message = message.toString();this.getName = function() {return this.name;};this.getMessage = function() {return this.message;}; }

上面的對(duì)象會(huì)有什么問題呢?

上面對(duì)象的問題就在于,對(duì)于每一個(gè)new出來的對(duì)象,getName和getMessage方法都會(huì)被復(fù)制一份,一方面是內(nèi)容的冗余,另一方面是性能的影響。

通常來說,我們將對(duì)象的方法定義在prototype上面:

function MyObject(name, message) {this.name = name.toString();this.message = message.toString(); } MyObject.prototype.getName = function() {return this.name; }; MyObject.prototype.getMessage = function() {return this.message; };

注意,我們不要直接重寫整個(gè)prototype,這樣會(huì)導(dǎo)致未知的錯(cuò)誤,我們只需要根據(jù)需要重寫特定的方法即可。

總結(jié)

閉包是JS中非常強(qiáng)大和有用的概念,希望大家能夠喜歡。

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/js-closure/

本文來源:flydean的博客

歡迎關(guān)注我的公眾號(hào):「程序那些事」最通俗的解讀,最深刻的干貨,最簡(jiǎn)潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!

總結(jié)

以上是生活随笔為你收集整理的javascript中的闭包closure详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。