javascript
javascript闭包—围观大神如何解释闭包
閉包的概念已經出來很長時間了,網上資源一大把,本著拿來主意的方法來看看。
這一篇文章?學習Javascript閉包(Closure)?是大神阮一峰的博文,作者循序漸進,講的很透徹。下面一一剖析。
1.變量的作用域
變量的作用域有局部和全局兩種,在javascript的函數內部可以訪問全局變量,如下:
// 函數內部可以直接讀取全局變量var n = 99;function f1() {alert(n);}f1();在f1函數中可以訪問到全局變量n。輸出如下:
反過來就不行了,在函數外部不能讀取函數內部的變量,例如這樣:
//函數外部無法讀取函數內部的局部變量,這里會報錯function f2() {var m = 99;}alert(m) //報錯javascript還有一個比較特殊的地方,在函數內部如果沒有使用var,const,let修飾符聲明變量,那么這個變量不再是局部變量而是一個全局變量,如下:
function f2() {m = 99;}f2();alert(m)輸出99:
是不是很神奇,但是這個經常給人造成困惑。
?
2.如何從外部讀取函數內部的局部變量
很多場合下要訪問函數內部的局部變量,變通的方式是在函數內部定義函數,如下:
function f3() {var a = 999;function f4() {alert(a);}return f4}var result = f3();result();函數f4包含在函數f3里面,所以f4范圍內可以訪問到f3中的那個變量a,反過來是不行的,Javascript語言特有的"鏈式作用域"結構(chain scope),子對象會一級一級地向上尋找所有父對象的變量。所以,父對象的所有變量,對子對象都是可見的,反之則不成立。
?
3.閉包的概念
可以簡單理解成定義在函數內部的函數,這個內部函數可以把內部函數作用域內的變量傳播到外面。由于在javascript中只有內部的子函數才能讀取局部變量,因此可以把閉包簡單的理解成“定義在一個函數內部的函數”。本質上,閉包就是將函數內部和函數外部連接起來的橋梁。
?
4.閉包的作用
閉包的第一個用處就是讀取函數內部的變量,另一個作用就是讓這些變量始終保存在內存中。來看下面的代碼:
function f5() {var b = 111;nAdd = function () {b += 1;}function f6() {alert(b);}return f6}var result1 = f5();result1();nAdd();result1();上面代碼兩次彈出框,第一次是輸出111,第二次是112,這就證明函數函數f5內的局部變量b一直保存在內存中,并沒有在f5調用后被自動清除。
這就說明,第一次調用result1();的時候給變量b賦值了,然后調用全局函數nAdd的時候變量b仍然還在內存中,給他加1就變成112了。原因就在于f5是f6的父函數,而f6被賦給了一個全局變量,這導致f6始終在內存中,而f6的存在依賴于f5,因此f5也始終在內存中,不會在調用結束后,被垃圾回收機制(garbage collection)回收。
這段代碼中另一個值得注意的地方,就是"nAdd=function(){n+=1}"這一行,首先在nAdd前面沒有使用var關鍵字,因此nAdd是一個全局變量,而不是局部變量。其次,nAdd的值是一個匿名函數(anonymous function),而這個匿名函數本身也是一個閉包,所以nAdd相當于是一個setter,可以在函數外部對函數內部的局部變量進行操作。
5.注意?
1)由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。其實一般不會這樣用!!!
2)閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。其實一般不會這樣用!!!
最后作者給出了思考題目,如下:
var name = 'The Window';var object = {name: 'My Object',getNameFunc: function () {// 這里的this是函數console.info(this)return function () {//匿名函數的執行環境是windowsconsole.info(this)return this.name;}}};alert(object.getNameFunc()());其實從變量的值已經看到答案了,在對象object內部的函數getNameFunc里面返回了一個匿名函數這個匿名函數的作用域是window,所以這里輸出的是'The Window',如下:
我在日志里面加上的調試語句可以看出端倪:
看下面的代碼:
var name1 = "The Window";var object1 = {name1: 'My Object1',getNameFunc:function () {var that = this;return function () {return that.name1}}}alert(object1.getNameFunc()());這里在函數內部使用var that = this語句先把當前上下文的對象保存下來,在匿名函數中使用that.name1,這樣就是當前對象中的name1,于是輸出了“MyObject1”。其實可以用es6中的箭頭函數,如下:
var name1 = "The Window";var object1 = {name1: 'My Object1',getNameFunc:function () {return () => {return this.name1}}}alert(object1.getNameFunc()());箭頭函數會綁定object1的作用域,于是仍任是object1的屬性name1,和上面輸出的結果一樣。
6.深入的理解
?在知乎上也有人在討論這個問題,知乎你懂的,比較嚴謹,如何通俗易懂的解釋javascript里面的‘閉包’?這一篇問答里有人給出了其他的解釋。
?1.每次定義一個函數,都會產生一個作用域鏈(scope chain)。當JavaScript尋找變量varible時(這個過程稱為變量解析),總會優先在當前作用域鏈的第一個對象中查找屬性varible ,如果找到,則直接使用這個屬性;否則,繼續查找下一個對象的是否存在這個屬性;這個過程會持續直至找到這個屬性或者最終未找到引發錯誤為止。
------有道理,關鍵是弄懂這個作用域鏈,說白了就是花括號的層級及各種函數的的上下文作用域,比如在函數中定義變量不用var,let它居然是全局的,這個是javascript比較特殊的地方,強類型語言估計早就報錯了。
2.JavaScript中的函數運行在它們被定義的作用域里,而不是它們被執行的作用域里。
------有道理,上面的最后一個代碼段中的例子,本來執行object1.getNameFunc()()這一句的時候,執行作用域中的name1是var name1 = "The Window";這個,但是彈出來的確實定義getNameFunc這個函數的作用域內的name1: 'My Object1',
3.子函數能夠訪問父函數的局部變量,反之則不行。而那個子函數就是閉包!
------有道理,就是上面阮大師反復說明的
好了,就這么多了。
?
轉載于:https://www.cnblogs.com/tylerdonet/p/6561334.html
總結
以上是生活随笔為你收集整理的javascript闭包—围观大神如何解释闭包的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: c#左移、右移基础
- 下一篇: OpenCL入门概念
