學習JavaScript閉包

最近工做之餘匆忙去參加了一個面試,感受多是一直在工做,不太注意去總結一些知識點,因此回答的我不是特別滿意,有一些專業術語和實際技術不能很快的聯繫起來,印象最深的就是閉包這個,JavaScript的一個特色,也算是一個難點吧,很容易說不清楚或者是理解有誤差,可是實際工做中,不少應用都須要依靠閉包來實現,所以,在此鞏固一下。javascript

1、變量做用域

首先,要理解閉包,必須理解JavaScript的變量做用域。html

JavaScript變量的做用域分爲兩種:全局變量和局部變量。java

什麼是全局變量呢?全局變量就是在函數外部聲明或者是不使用var命令直接聲明的變量,全局變量做爲window的一個屬性。面試

什麼是局部變量呢?局部變量就是在函數內部定義的變量。閉包

1.函數內部能夠直接讀取全局變量。函數

var str = "Hello World!";
function fuc() { alert(str); }
fuc(); //Hello World

2.函數外部沒法讀取局部變量。性能

 function fuc() {
        var str = 'Hello World';
    }
alert(str); // error

Note:必定要養成使用var命令聲明變量的習慣,由於若是在函數內部聲明變量(指望聲明一個局部變量),可是不適用var命令的話,實際上聲明瞭一個全局變量。學習

function fuc() { str = "Hello World"; }
fuc();
alert(str); //Hello World

2、如何在函數外部讀取局部變量?

上面說過,函數外部沒法讀取局部變量,可是,有些狀況下咱們須要獲得函數內部的局部變量怎麼辦?this

1.函數內部再定義一個函數:spa

function fuc() {
     var str = "Hello World";

     function fuc2() {
          alert(str);
      }

       return fuc2;
}
var result = fuc();
result(); //Hello World

這個示例中,fuc2函數被包括在函數fuc內部,這時候fuc函數內部的全部局部變量,對於fuc2都是可見的。可是fuc2內部的局部變量對於fuc則是不可見的。這就是JavaScript語言特有的「鏈式做用域」結構(chain scope),子對象會一級一級地向上尋找全部父對象的變量。因此,父對象的全部變量,對子對象都是可見的,反之則不成立。

因此,既然fuc2能夠讀取fuc中的局部變量,那麼只要把fuc2做爲返回值,咱們就能夠在fuc外部讀取它的內部變量了。

3、閉包

1.概念

什麼是閉包呢?閉包就是可以讀取其餘函數內部變量的函數。在JavaScript語言中,只有函數內部的子函數才能讀取局部變量,所以閉包也能夠理解成「定義在一個函數內部的函數」。因此,本質上,閉包就是將函數內部和函數外部鏈接起來的一座橋樑。

2.閉包的用途

(1)、在函數外部讀取函數內部的變量

(2)、讓函數內部的變量的值始終保持在內存中

function f1() {
    var n = 0;
    nAdd = function () { n += 1 }
    function f2() {
        alert(n);
    }
    return f2;
}
var result = f1();
result(); // 0
nAdd();
result(); // 1

此段代碼,result就是閉包f2函數。它運行了兩次,第一次的值是0,第二次的值是1.這證實了,函數f1中的局部變量n一直保存在內存中,並無在f1調用後被自動清除。

那麼爲何會這樣呢?緣由在於f1是f2的父函數,而f2被賦給了一個全局變量,這致使f2始終在內存中,而f2的存在依賴於f1,所以f1也始終在內存中,不會在調用結束後,被垃圾回收機制(garbage collection)回收。

這段代碼中另外一個值得注意的地方,就是"nAdd=function{n+=1;}"這一行,首先在nAdd前面沒有使用var關鍵字,所以nAdd是一個全局變量,而不是局部變量。其次,nAdd的值是一個匿名函數(anonymous function),而這個匿名函數自己也是一個閉包,因此nAdd至關因而一個setter,能夠在函數外部對函數內部的變量進行操做。

4、使用閉包的注意事項

一、因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄漏。解決辦法是,在退出函數以前,將不使用的局部變量所有刪除。

二、閉包會在父函數外部,改變父函數內部變量的值。因此,若是你把父函數當作對象(Object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這是必定要當心,不要隨便改變父函數內部變量的值。

5、練習題

練習一:

var name = "The Window";
var obj = {
     name: "My Object",
     getNameFunc: function () {
            //this是指obj對象
            return function () {
            //this是指window對象
            return this.name; 
        };
    }
};
alert(obj.getNameFunc()());

練習二:

var name = "The Window";
var object = {
    name: "My Object",
    getNameFunc: function () {
        var that = this;
        return function () {
            return that.name;
        };
    }
};
alert(object.getNameFunc()());

這兩個練習題有一個比較值得注意的地方,this,通常而言,在JavaScript中,this指向函數執行時的當前對象,可是當沒有明確的對象的時候,this就是指向全局對象window。

5.參考連接

[1] 阮一峯, 學習Javascript閉包(Closure)

相關文章
相關標籤/搜索