nodejs閉包

一、什么是閉包?

官方”的解釋是:閉包是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。
相信很少有人能直接看懂這句話,因為他描述的太學術。其實這句話通俗的來說就是:JavaScript中所有的function都是一個閉包。不過一般來說,嵌套的function所產生的閉包更為強大,也是大部分時候我們所謂的“閉包”。看下面這段代碼:

function a() { var i = 0; function b() { alert(++i); } return b;
}
var c = a();
c();

這段代碼有兩個特點:

1、函數b嵌套在函數a內部;

2、函數a返回函數b。

引用關系如圖:

這樣在執行完var c=a()后,變量c實際上是指向了函數b,再執行c()后就會彈出一個窗口顯示i的值(第一次為1)。這段代碼其實就創建了一個閉包,為什么?因為函數a外的變量c引用了函數a內的函數b,就是說:

當函數a的內部函數b被函數a外的一個變量引用的時候,就創建了一個閉包。

讓我們說的更透徹一些。所謂“閉包”,就是在構造函數體內定義另外的函數作為目標對象的方法函數,而這個對象的方法函數反過來引用外層函數體中的臨時變量。這使得只要目標 對象在生存期內始終能保持其方法,就能間接保持原構造函數體當時用到的臨時變量值。盡管最開始的構造函數調用已經結束,臨時變量的名稱也都消失了,但在目 標對象的方法內卻始終能引用到該變量的值,而且該值只能通這種方法來訪問。即使再次調用相同的構造函數,但只會生成新對象和方法,新的臨時變量只是對應新 的值,和上次那次調用的是各自獨立的。

二、閉包有什么作用?

簡而言之,閉包的作用就是在a執行完并返回后,閉包使得Javascript的垃圾回收機制GC不會收回a所占用的資源,因為a的內部函數b的執行需要依賴a中的變量。這是對閉包作用的非常直白的描述,不專業也不嚴謹,但大概意思就是這樣,理解閉包需要循序漸進的過程。

在上面的例子中,由于閉包的存在使得函數a返回后,a中的i始終存在,這樣每次執行c(),i都是自加1后alert出i的值。

那 么我們來想象另一種情況,如果a返回的不是函數b,情況就完全不同了。因為a執行完后,b沒有被返回給a的外界,只是被a所引用,而此時a也只會被b引 用,因此函數a和b互相引用但又不被外界打擾(被外界引用),函數a和b就會被GC回收。(關于Javascript的垃圾回收機制將在后面詳細介紹)

三、閉包內的微觀世界

如果要更加深入的了解閉包以及函數a和嵌套函數b的關系,我們需要引入另外幾個概念:函數的執行環境(excution context)、活動對象(call object)、作用域(scope)、作用域鏈(scope chain)。以函數a從定義到執行的過程為例闡述這幾個概念。

當定義函數a的時候,js解釋器會將函數a的作用域鏈(scope chain)設置為定義a時a所在的“環境”,如果a是一個全局函數,則scope chain中只有window對象。
當執行函數a的時候,a會進入相應的執行環境(excution context)。
在創建執行環境的過程中,首先會為a添加一個scope屬性,即a的作用域,其值就為第1步中的scope chain。即a.scope=a的作用域鏈。
然后執行環境會創建一個活動對象(call object)。活動對象也是一個擁有屬性的對象,但它不具有原型而且不能通過JavaScript代碼直接訪問。創建完活動對象后,把活動對象添加到a的作用域鏈的最頂端。此時a的作用域鏈包含了兩個對象:a的活動對象和window對象。
下一步是在活動對象上添加一個arguments屬性,它保存著調用函數a時所傳遞的參數。
最后把所有函數a的形參和內部的函數b的引用也添加到a的活動對象上。在這一步中,完成了函數b的的定義,因此如同第3步,函數b的作用域鏈被設置為b所被定義的環境,即a的作用域。
到此,整個函數a從定義到執行的步驟就完成了。此時a返回函數b的引用給c,又函數b的作用域鏈包含了對函數a的活動對象的引用,也就是說b可以訪問到a中定義的所有變量和函數。函數b被c引用,函數b又依賴函數a,因此函數a在返回后不會被GC回收。

當函數b執行的時候亦會像以上步驟一樣。因此,執行時b的作用域鏈包含了3個對象:b的活動對象、a的活動對象和window對象,如下圖所示:

如圖所示,當在函數b中訪問一個變量的時候,搜索順序是:

先搜索自身的活動對象,如果存在則返回,如果不存在將繼續搜索函數a的活動對象,依次查找,直到找到為止。
如果函數b存在prototype原型對象,則在查找完自身的活動對象后先查找自身的原型對象,再繼續查找。這就是Javascript中的變量查找機制。
如果整個作用域鏈上都無法找到,則返回undefined。
小結,本段中提到了兩個重要的詞語:函數的定義與執行。文中提到函數的作用域是在定義函數時候就已經確定,而不是在執行的時候確定(參看步驟1和3)。用一段代碼來說明這個問題:

function f(x) { var g = function () { return x; }return g;
}
var h = f(1);
alert(h());

這段代碼中變量h指向了f中的那個匿名函數(由g返回)。

假設函數h的作用域是在執行alert(h())確定的,那么此時h的作用域鏈是:h的活動對象->alert的活動對象->window對象。
假設函數h的作用域是在定義時確定的,就是說h指向的那個匿名函數在定義的時候就已經確定了作用域。那么在執行的時候,h的作用域鏈為:h的活動對象->f的活動對象->window對象。
如果第一種假設成立,那輸出值就是undefined;如果第二種假設成立,輸出值則為1。

運行結果證明了第2個假設是正確的,說明函數的作用域確實是在定義這個函數的時候就已經確定了。

四、閉包的應用場景

保護函數內的變量安全。以最開始的例子為例,函數a中i只有函數b才能訪問,而無法通過其他途徑訪問到,因此保護了i的安全性。

在內存中維持一個變量。依然如前例,由于閉包,函數a中i的一直存在于內存中,因此每次執行c(),都會給i自加1。
通過保護變量的安全實現JS私有屬性和私有方法(不能被外部訪問)
私有屬性和方法在Constructor外是無法被訪問的

function Constructor(...){  var that = this;  var membername = value; function membername(...){...}
}

以上3點是閉包最基本的應用場景,很多經典案例都源于此。

五、Javascript的垃圾回收機制

在Javascript中,如果一個對象不再被引用,那么這個對象就會被GC回收。如果兩個對象互相引用,而不再被第3者所引用,那么這兩個互相引用的對象也會被回收。因為函數a被b引用,b又被a外的c引用,這就是為什么函數a執行后不會被回收的原因。

關于this指針

//第一種情況,全局中的this指向的是module.exports。console.log(this); //{}
this.num = 10;
console.log(this.num); //10
console.log(global.num); //undefined
console.log(module.exports.num); //10

//第二種情況,在函數中this指向的是global對象,和全局中的this不是同一個對象,簡單來說,你在函數中通過this定義的變量就是相當于給global添加了一個屬性,此時與全局中的this已經沒有關系了。

//例子一:
function fn(){
this.num = 20;
}
fn();
console.log(this); //{}
console.log(this.num); //undefined
console.log(global.num); //20//例子二
function fn(){function fn2(){this.age = 18;}fn2();console.log(this); //globalconsole.log(this.age); //18console.log(global.age); //18
}
fn();

//第三種情況,在構造函數中this指向的是它的實例,而不是global。

function Fn(){this.num = 998;
}
var fn = new Fn();
console.log(this); //{}
console.log(fn.num); //998
console.log(global.num); //undefined

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/249432.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/249432.shtml
英文地址,請注明出處:http://en.pswp.cn/news/249432.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

《深入理解Java虛擬機》讀書筆記八

第九章 類加載及執行子系統的案例與實戰 Q:如果有10個WEB應用程序都是用Spring來進行組織管理的話,可以把Spring放到Common或Shared目錄下(Tomcat5.0)讓這些程序共享。Spring要對用戶程序的類進行管理,自然要能訪問到用…

一些非常有用的鏈接和工具

微信公眾平臺SDK Senparc.Weixin for C#,支持.NET Framework及.NET Core : https://github.com/JeffreySu/WeiXinMPSDK layui開發文檔地址:https://www.layui.com/doc/ .Net Core GitHub社區 : https://github.com/dotnetcore EF…

Activity Intent相關FLAG介紹

先首先簡單介紹下Task和Activity的關系 Task就像一個容器,而Activity就相當與填充這個容器的東西,第一個東西(Activity)則會處于最下面,最后添加的東西(Activity)則會在最上面。從Task中取出東西…

js的原型和原型鏈

構造函數創建對象: function Person() {} var person new Person(); person.name Kevin; console.log(person.name) // KevinPerson 就是一個構造函數,我們使用 new 創建了一個實例對象 person prototype 每個函數都有一個 prototype 屬性 每一個Ja…

二維數組

要求:求一個二維數組的最大子數組和 思路:對于這個題,我會最簡單的讀取,雖然在網上查到了代碼,但是查找最大子數組的循環我真的看不懂,也不是特別懂思路,所以在這不會寫思路 package 二維數組; …

資源

資源鏈接: 內存池TinySTLminiSTLcghSTL1. lishuhuakai 2. 轉載于:https://www.cnblogs.com/sunbines/p/9707483.html

Android判斷應用或Activity是否存在

一、根據包名判斷應用是否存在public boolean checkApplication(String packageName) { if (packageName null || "".equals(packageName)){ return false; } try { ApplicationInfo info getPackageManager().getApplicationInfo(packageName, PackageManager.GET…

vue ref

https://www.jianshu.com/p/623c8b009a85

033 Url中特殊字符的處理

在url跳轉頁面的時候,參數值中的#不見了,一直沒有處理,今天有空看了一下,后來發現后臺的過濾器之類的都沒有處理,就比較奇怪了,原來是特殊字符的問題。 一:Url中的特殊字符 1.說明 這里還是需要…

Effective Java(1)-創建和銷毀對象

Effective Java(1)-創建和銷毀對象 轉載于:https://www.cnblogs.com/Johar/p/10556218.html

什么是Affinity

什么是Affinity 在某些情況下,Android需要知道一個Activity屬于哪個Task,即使它沒有被啟動到一個具體的Task里。這是通過任務共用性(Affinities)完成的。任務共用性(Affinities)為這個運行一個或多…

vue this

https://blog.csdn.net/cddcj/article/details/80866902

課外書——自控力(斯坦福大學最受歡迎的心理學課程)

01我要做,我不要,我想要:什么是意志力?為什么意志力?為什么意志力至關重要? 核心思想: 意志力實際上是“我要做”、“我不要”和“我想要”這三種力量。它們協同努力,讓我們變成更好…

Docker運行GUI軟件的方法

轉自 https://www.csdn.net/article/2015-07-30/2825340 簡介: Docker通過namespace將容器與主機上的網絡和運行環境進行了隔離,默認情況下,在容器中運行帶界面的軟件在外部是看不到的。在這個分享中,將介紹通過共享X11套接字讓外…

JS正則表達式驗證數字非常全

驗證數字的正則表達式集 驗證數字:^[0-9]*$ 驗證n位的數字:^\d{n}$ 驗證至少n位數字:^\d{n,}$ 驗證m-n位的數字:^\d{m,n}$ 驗證零和非零開頭的數字:^(0|[1-9][0-9]*)$ 驗證有兩位小數的正實數:^[0-9](.[0-9…

VMware VIC

vSphere Integrated Containers - a short intro High-Level view of VCH Networking vSphere Integrated Containers Roles and Personas 參考鏈接:https://vmware.github.io/vic-product/assets/files/html/1.4/轉載于:https://www.cnblogs.com/vincenshen/p/9715…

vue new vue

https://www.jianshu.com/p/5ca5f40e4810

MySQL 之group_concat_max_length Mac 版

用過MySQL的人都知道,group_concat這個函數是有最大值限制的,當超過了最大值就會報錯! 在window下的處理方法就是修改MySQL的配置文件my.ini,在其中添加 #group_concat_max_len setting group_concat_max_len 1024000000 然…

Locust學習總結分享

簡介: Locust是一個用于可擴展的,分布式的,性能測試的,開源的,用Python編寫框架/工具,它非常容易使用,也非常好學。它的主要思想就是模擬一群用戶將訪問你的網站。每個用戶的行為由你編寫的py…

IDEA系列(四)一部署war 和 war exploded的區別

war模式:將WEB工程以包的形式上傳到服務器 ; war exploded模式:將WEB工程以當前文件夾的位置關系上傳到服務器;(1)war模式這種可以稱之為是發布模式,看名字也知道,這是先打成war包&a…