我來給Javascript中的閉包下個定義

1 閉包的定義

維持了自由變量不被釋放的函數, 稱爲閉包,(自由變量指不在自身上下文,也不在全局上下文中的變量)。閉包

那麼閉包函數的特色在哪裏,咱們知道函數在建立的時候,它的[[scope]]屬性就已經肯定並不能夠改變,因此閉包函數在建立的時候就保存了上級的做用域鏈,閉包函數經過做用域鏈去尋找使用到的變量,正常狀況下,函數在執行完畢後,將銷燬函數的執行上下文,可是因爲閉包函數的存在,包含閉包函數的上級函數執行完畢後,若是閉包函數還存在,那麼這個上級函數的做用域中的變量仍然保留在內存中供閉包函數訪問。函數

注意:
由定義能夠知道,閉包函數確定是定義在函數中,纔可能有上級的函數做用域能夠訪問,不然上級做用域就是全局做用域。全局做用域中的變量自己就一直在內存中,因此訪問全局做用域中變量的函數不能稱爲閉包。code

var name = 'global';   
function func1() {
    var name1 = 'func1';
    console.log(name);
    console.log(name);
    function func2() {
        console.log(name);
        var name2 = 'func2';
        function func3() {
            console.log(name2);
        }
        func3();
    }
    func2();
}
func1();

上面代碼中,func3爲閉包函數,由於它訪問了上級函數做用域中的變量name2,func2不能稱爲閉包函數,由於它們訪問的是全局做用域中的變量name。內存

2 常見的閉包場景

2.1 維持變量的閉包

var person = (function(){
    var _name = 'yangyiliang';
    var _age = 18;
    return {
        getName: function () {
            return _name;
        },
        getAge: function () {
            return _age;
        },
        addAge: function (num) {
            return _age += num;
        },
        reduceAge: function (num) {
            return _age -= num;
        }
    }
})();
 
console.log(person.addAge(5)); // 23
console.log(person.reduceAge(3)); //20

上面的代碼中,首先外層包裹了一個匿名當即執行函數,創造了一個上級函數做用域,getName和getAge方法都是在其內部,而且訪問了上級函數做用域中的變量,因此是閉包,所當匿名函數執行完畢後,本該銷燬的執行上下文,卻由於閉包函數而保留了做用域中的_name和 _age變量, 經過addAge 和reduceAge的結果發現,兩個閉包共用保留的做用域。作用域

2.2 維持參數的閉包

function makeSizer(size) {
    return function() {
        document.body.style.fontSize = size + 'px';
    };
}
 
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
 
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

上面的代碼中,makerSizer函數的返回值即爲閉包函數,閉包函數訪問上級函數做用域中的參數。
2.3 循環建立閉包常見錯誤get

function bind() {
    var arr = document.getElementsByTagName("p");
    for(var i = 0; i < arr.length;i++){
       arr[i].onclick = function(){
            alert(i);
       }
    }
}  
bind();

上面的代碼中,假設arr的length爲5,想要實現的功能是點擊5個P標籤分別alert 0,1,2,3,4。可是事實上獲得的結果倒是都alert 5。io

形成這種結果的緣由就是綁定的多個onclick函數是閉包函數,他們共同使用保留的上級函數做用域中的變量 i 。 for循環執行結束後 i 的值即爲5。console

要想解決這種錯誤,就讓這些閉包保存不一樣的上級做用域便可。for循環

function bind() {
    var arr = document.getElementsByTagName("p");
    for(var i = 0; i < arr.length;i++){
        (function (i) {
            arr[i].onclick = function(){
            alert(i);
            }
        })(i);
    }
}  
bind();
或者
function bind() {
    var arr = document.getElementsByTagName("p");
    for(var i = 0; i < arr.length;i++){
        arr[i].onclick = (function(i){
            return function () {
                alert(i);
            }
        })(i);
    }
}  
bind();
相關文章
相關標籤/搜索