【JavaScript】【函數】閉包閉包!

閉包,有人說它是一種設計理念,有人說全部的函數都是閉包。我不知道如何去定義它,我也不許備去定義它,定義它就是限制了對它的理解。segmentfault

咱們依賴光來看清世間萬物,光卻遮住了黑暗。如同你腳下的路,讓你看不清前行的方向。數組

在這裏寫一點我對閉包的理解。理解閉包的關鍵在於:外部函數調用以後其變量對象本應該被銷燬,但閉包的存在使咱們仍然能夠訪問外部函數的變量對象。閉包

function outer() {
    var a = 1;
    return function() {
        return a;
    };
}
var b = outer();
console.log(b()); //1

模塊級做用域

(function(){
    var now = new Date();
    if (now.getMonth() == 0 && now.getDate() == 1) {
        alert('Happy new Year!');
    }
})();

這種方式經常使用來限制向全局做用域添加過多的變量和函數。app

建立私有變量

(funcion() {
    var a = 1;
    setA = function(val){
        a = val;
    };
    getA = function(){
        return a;
    };
})();
console.log(a); //報錯
console.log(getA()); //1
setA(2);
console.log(getA()); //2

也能夠這樣寫:函數

function outer(){
    var a = 1;
    return {
        setA: function(val) {
            a = val;  
        },
        getA: function() {
            return a;
        }
    };
}
var closure = outer();
console.log(a); //報錯
console.log(closure.getA()); //1
closure.setA(2);
console.log(closure.getA()); //2

第一個例子中,setAgetA都是全局變量,用於讀寫私有變量a。第二個例子中將這兩個方法做爲一個對象返回並賦給一個全局變量closure,這樣當outer執行完畢後,這兩個方法(匿名函數)連同外部函數中的變量對象(a)依然不會被銷燬。this

閉包用於建立具備私有變量的實例對象,參考經過閉包建立具備私有屬性的實例對象設計

閉包只能取得包含函數中任何變量的最後一個值

function arrFunc() {
    var arr = [];
    for (var i=0; i<10; i++) {
        arr[i] = function() {
            return i;
        };
    }
    return arr;
}

arr數組中包含了10個匿名函數,每一個匿名函數都能訪問外部函數的變量i,那麼i是多少呢?當arrFunc執行完畢後,其做用域被銷燬,但它的變量對象仍保存在內存中,得以被匿名訪問,這時i的值爲10。要想保存在循環過程當中每個i的值,須要在匿名函數外部再套用一個匿名函數,在這個匿名函數中定義另外一個變量而且當即執行來保存i的值。code

function arrFunc() {
    var arr = [];
    for (var i=0; i<10; i++) {
        arr[i] = function(num) {
            return function() {
                return num;
            };
        }(i);
    }
    return arr;
}
console.log(arrFunc()[1]()); //1

這時最內部的匿名函數訪問的是num的值,因此數組中10個匿名函數的返回值就是1-10。對象

閉包中的this

var name = 'window';
var obj = {
    name: 'object',
    getName: function() {
        return function() {
            return this.name;
        };
    }
};
console.log(obj.getName()()); //window

obj.getName()()其實是在全局做用域中調用了匿名函數,this指向了window。這裏要理解函數名與函數功能是分割開的,不要認爲函數在哪裏,其內部的this就指向哪裏。window纔是匿名函數功能執行的環境。要想使this指向外部函數的執行環境,能夠這樣改寫:內存

var name = 'window';
var obj = {
    name: 'object',
    getName: function() {
        var that = this;
        return function() {
            return that.name;
        };
    }
};
console.log(obj.getName()()); //object

argumentsthis也有相同的問題。下面的狀況也要注意:

var name = 'window';
var obj = {
    name: 'object',
    getName: function() {
        return this.name;
    }
};
obj.getName(); //object
(obj.getName = obj.getName)(); //window 非嚴格模式下

obj.getName();這時getName()是在對象obj的環境中執行的,因此this指向obj
(obj.getName = obj.getName)賦值語句返回的是等號右邊的值,在全局做用域中返回,因此(obj.getName = obj.getName)();this指向全局。要把函數名和函數功能分割開來

內存泄漏

閉包會引用包含函數的整個變量對象,若是閉包的做用域鏈中保存着一個HTML元素,那麼就意味着該元素沒法被銷燬。因此咱們有必要在對這個元素操做完以後主動銷燬。

function assignHandler() {
    var element = document.getElementById('someElement');
    var id = element.id;
    element.onclick = function() {
        alert(id);
    };
    element = null;
}

函數內部的定時器

當函數內部的定時器引用了外部函數的變量對象時,該變量對象不會被銷燬。

(function() {
    var a = 0;
    setInterval(function(){
        console.log(a++);
    }, 1000);
})();

運用閉包的關鍵

  1. 閉包引用外部函數變量對象中的值;

  2. 在外部函數的外部調用閉包。

轉載請註明出處:http://www.javashuo.com/article/p-dwwykxew-cv.html

文章不按期更新完善,若是能對你有一點點啓發,我將不勝榮幸。

相關文章
相關標籤/搜索