一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務器之家 - 編程語言 - JavaScript - js教程 - javascript中閉包closure的深入講解

javascript中閉包closure的深入講解

2022-01-25 16:39flydean js教程

這篇文章主要給大家介紹了關于javascript中閉包closure的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

簡介

閉包closure是javascript中一個非常強大的功能。所謂閉包就是函數中的函數,內部函數可以訪問外部函數的作用域范圍,從而可以使用閉包來做一些比較強大的工作。

今天將會給大家詳細介紹一下閉包。

函數中的函數

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

?
1
2
3
4
5
6
7
8
function parentFunction() {
 var address = 'flydean.com';
 function alertAddress() {
 alert(address);
 }
 alertAddress();
}
parentFunction();

上面的例子中,我們在parentFunction中定義了一個變量address,在parentFunction內部定義了一個alertAddress方法,在該方法內部訪問外部函數中定義的address變量。

上面代碼運行是沒問題的,可以正確的訪問到數據。

Closure閉包

函數中的函數有了,那么什么是閉包呢?

我們看下面的例子:

?
1
2
3
4
5
6
7
8
9
function parentFunction() {
 var address = 'flydean.com';
 function alertAddress() {
 alert(address);
 }
 return alertAddress;
}
var myFunc = parentFunction();
myFunc();

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

接下來我們直接調用了myFunc。

myFunc中訪問了parentFunction中的address變量,雖然parentFunction已經執行完畢返回。

但是我們在調用myFunc的時候,任然可以訪問到address變量。這就是閉包。

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

?
1
2
3
4
5
6
7
8
9
10
11
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這個function factory創建出來的。通過傳遞不同的x參數,我們得到了不同的基數的add方法。

最終生成了兩個不同的add方法。

使用function factory的概念,我們可以考慮一個閉包的實際應用,比如我們在頁面上有三個button,通過點擊這些button可實現修改字體的功能。

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

?
1
2
3
4
5
6
7
8
9
function makeSizer(size) {
 return function() {
 document.body.style.fontSize = size + 'px';
 };
}
 
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

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

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

使用閉包實現private方法

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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.

我們在父function中定義了privateCounter屬性和changeBy方法,但是這些方法只能夠在內部function中訪問。

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

閉包的Scope Chain

對于每個閉包來說,都有一個作用域范圍,包括函數本身的作用域,父函數的作用域和全局的作用域。

如果我們在函數內部嵌入了新的函數,那么就會形成一個作用域鏈,我們叫做scope chain。

看下面的一個例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// global scope
var e = 10;
function sum(a){
 return function(b){
 return function(c){
  // outer functions scope
  return function(d){
  // local scope
  return a + b + c + d + e;
  }
 }
 }
}
 
console.log(sum(1)(2)(3)(4)); // log 20

閉包常見的問題

第一個常見的問題就是在循環遍歷中使用閉包,我們看一個例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();

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

因為在循環里面賦值,所以我們實際上創建了3個閉包,但是這3個閉包共享的是同一個外部函數的作用域范圍。

我們的本意是,不同的id觸發不同的help消息。但是如果我們真正執行就會發現,不管是哪一個id,最終的消息都是最后一個。

因為onfocus是在閉包創建完畢之后才會觸發,這個時候item的值實際上是變化的,在循環結束之后,item的值已經指向了最后一個元素,所以全部顯示的是最后一條數據的help消息。

怎么解決這個問題呢?

最簡單的辦法使用ES6中引入的let描述符,從而將item定義為block的作用域范圍,每次循環都會創建一個新的item,從而保持閉包中的item的值不變。

?
1
2
3
4
5
6
for (let i = 0; i < helpText.length; i++) {
let item = helpText[i];
document.getElementById(item.id).onfocus = function() {
 showHelp(item.help);
}
}

還有一種方法,就是再創建一個閉包:

?
1
2
3
4
5
6
7
8
9
10
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的概念,我們為不同的閉包創建了不同的作用域環境。

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

?
1
2
3
4
5
6
7
8
for (var i = 0; i < helpText.length; i++) {
 (function() {
   var item = helpText[i];
   document.getElementById(item.id).onfocus = function() {
    showHelp(item.help);
   }
 })();
}

第二個常見的問題就是內存泄露。

?
1
2
3
4
5
6
7
8
9
function parentFunction(paramA)
{
var a = paramA;
function childFunction()
{
return a + 2;
}
return childFunction();
}

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

閉包性能的問題

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

?
1
2
3
4
5
6
7
8
9
10
11
function MyObject(name, message) {
 this.name = name.toString();
 this.message = message.toString();
 this.getName = function() {
  return this.name;
 };
 
 this.getMessage = function() {
  return this.message;
 };
}

上面的對象會有什么問題呢?

上面對象的問題就在于,對于每一個new出來的對象,getName和getMessage方法都會被復制一份,一方面是內容的冗余,另一方面是性能的影響。

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

?
1
2
3
4
5
6
7
8
9
10
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;
};

注意,我們不要直接重寫整個prototype,這樣會導致未知的錯誤,我們只需要根據需要重寫特定的方法即可。

總結

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

到此這篇關于javascript中閉包closure的文章就介紹到這了,更多相關javascript閉包closure內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/flydean/p/14470911.html

延伸 · 閱讀

精彩推薦
  • js教程TypeScript類型體操:索引類型的映射再映射

    TypeScript類型體操:索引類型的映射再映射

    TypeScript 給 JavaScript 加了套靜態類型系統。其中,JavaScript 中的數組、對象等聚合多個元素的類型在 TypeScript 中對應的是索引類型。...

    神光的編程秘籍7752022-01-20
  • js教程JavaScript實現點擊切換功能

    JavaScript實現點擊切換功能

    這篇文章主要為大家詳細介紹了JavaScript實現點擊切換功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    _he_dan_10442022-01-10
  • js教程mapboxgl實現帶箭頭軌跡線的代碼

    mapboxgl實現帶箭頭軌跡線的代碼

    這篇文章主要介紹了mapboxgl實現帶箭頭軌跡線的代碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下...

    GIS兵器庫9232021-12-27
  • js教程JavaScript代碼實現微博批量取消關注功能

    JavaScript代碼實現微博批量取消關注功能

    這篇文章主要介紹了JavaScript代碼實現微博批量取消關注功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需...

    nbody19967192022-01-17
  • js教程js實現Element中input組件的部分功能并封裝成組件(實例代碼)

    js實現Element中input組件的部分功能并封裝成組件(實例代碼)

    這篇文章主要介紹了純生js實現Element中input組件的部分功能(慢慢完善)并封裝成組件,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借...

    背著泰山找黃河10522022-01-25
  • js教程ES5和ES6中類的區別總結

    ES5和ES6中類的區別總結

    這篇文章主要給大家介紹了ES5和ES6中類的區別的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    Totora612232021-12-16
  • js教程js實現驗證碼干擾(靜態)

    js實現驗證碼干擾(靜態)

    這篇文章主要為大家詳細介紹了js實現驗證碼干擾,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    lzh~5212022-01-22
  • js教程微信小程序自定義支持圖片的彈窗

    微信小程序自定義支持圖片的彈窗

    這篇文章主要為大家詳細介紹了微信小程序自定義支持圖片的彈窗,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    歲末Zzz8332021-12-15
主站蜘蛛池模板: 日韩一区二区三区四区不卡 | 国产免费成人在线视频 | 俄罗斯烧性春三级k8播放 | 2019男人天堂 | 日韩精品一区二区三区中文字幕 | 亚洲国产在线 | 免费午夜剧场 | 日本人和黑人一级纶理片 | 99久热只有精品视频免费观看17 | 国产精品对白刺激久久久 | 精品一区二区三区免费站 | 青久久 | 隔壁老王国产在线精品 | 92国产福利久久青青草原 | 亚洲精品九色在线网站 | 热99在线视频 | 精品一区视频 | 亚洲日本中文字幕在线2022 | 黄在线观看www免费看 | 蜜桃视频一区二区 | 无套啪啪 | 亚洲精品一区在线观看 | 91在线视频国产 | 国产不卡视频 | 亚洲欧美日韩精品高清 | 99精品国产自在现线观看 | 91大神精品 | 亚洲成人第一页 | 亚洲码和乱人伦中文一区 | 成年无限观看onlyfans | 黑人biglackon10十 | 日本伊人色综合网 | 久久精品热在线观看30 | 免费看打屁股视频的软件 | free性videoxxⅹ印度 | 男生和女生搞逼逼 | 92福利网| www.日本在线播放 | 国产aⅴ一区二区三区 | 亚洲男人的天堂网站 | 精品久久久久久久久久久久久久久 |