閉包函數

面試的時候一直會被問到什麼是閉包,之前也不是很在乎,更沒有去總結和概括.javascript

閉包就是可以讀取其餘函數內部變量的函數。
因此,在本質上,閉包就是將函數內部和函數外部鏈接起來的一座橋樑。java

(一)閉包最基本的應用:面試

少廢話,上代碼

仍是<<javascript高級程序設計>>的栗子,
function createComparisonFunction(propertyName) {
    return function(object1, object2) {
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];

        if(value1 < value2) {
            return -1;
        } else if(value1 > value2) {
            return 1;
        } else {
            return 0;
        }
    };
}
var compare = createComparisonFunction("name"); 

var result = compare({ name: "Nicholas" }, { name: "Greg" });

分析:
(1)閉包函數能夠訪問其外部函數
return出來的這個匿名函數就是一個閉包函數,這個匿名函數中的訪問了外部函數的活動對象,就是這個propertyName參數。由於外部的做用域鏈被這個匿名函數所包含(也能夠理解爲:compare函數包含
createComparisonFunction()函數的活動對象和全局變量對象),因此返回的這個匿名函數能夠一直訪問他外部的這個propertyName以及全局變量。
(2)閉包所引用的外部變量不會由於所在做用域銷燬而銷燬
由於在返回的閉包函數中,引用了外部函數的活動對象,因此createComparisonFunction()內的活動對象(即propertyName)在createComparisonFunction()執行完後不會被銷燬。由於雖然createComparisonFunction執行完後,會把其執行環境中的做用域鏈銷燬,可是他的活動對象仍然被閉包函數引用,放入了匿名函數的執行環境的做用域中數組


(二)閉包的反作用閉包

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

function createFunctions(){ 
            var result = new Array(); 
            for (var i=0; i < 10; i++){ 
                result[i] = function(){ 
                return i; 
                }; 
            } 
            return result; 
        }
        
        var res = createFunctions();
        console.log(res[0]());  //10
        console.log(res[1]());  //10

原理:
由於每一個函數的做用域鏈中都保存着 createFunctions() 函數的活動對象,因此它們引用的都是同一個變量 i 。 當createFunctions()函數返回後,變量 i 的值是 10,此時每一個函數都引用着保存變量 i 的同一個變量對象,因此在每一個函數內部 i 的值都是 10this

解決方法:設計

獲取內部函數的對象result[i]時,使用匿名函數,並在匿名函數中再使用閉包函數,使得當前環境下的num被閉包函數函數調用,存儲在做用域中不會被釋放.
function  createFunctions2(){
            var result  = new Array();
            for(var i = 0 ; i <10 ; i++){
                result[i] = (function(num){
                    return function(){
                        return num
                    }
                })(i)
            }
            return result;
        }
        
        var res2 =  createFunctions2();
        console.log(res2[0]());  // 0 
        console.log(res2[1]());  // 1

原理:
定義了一個匿名函數,並將當即執行該匿名函數的結果賦給數組。這裏的匿名函數有一個參數 num,也就是最終的函數要返回的值。在調用每一個匿名函數時,咱們傳入了變量 i。因爲函數參數是按值傳遞的,因此就會將變量 i 的當前值複製給參數 num。而在這個匿名函數內部,又建立並返回了一個訪問 num 的閉包。這樣一來,result 數組中的每一個函數都有本身num 變量的一個副本,所以就能夠返回各自不一樣的數值了。code

(2).當閉包函數外部包含了一個匿名函數,this指向全局對象

var name = "The Window";
        var object = {
            name: "My Object",
            getNameFunc: function () {   
                return function () {        //匿名函數執行具備全局性
                    return this.name;       //this指向window
                };
            }
        };

        console.log(object.getNameFunc()())  //  The Window

原理:
每一個函數在被調用時都會自動取得兩個特殊變量:this 和 arguments。內部函
數在搜索這兩個變量時,只會搜索到其活動對象爲止,因此沒法獲取到外部的this,此時getNameFunc()返回的是一個匿名函數,而且匿名函數具備全局性,所以this指向全局的window

解決方案:
把外部做用域中的 this 對象保存在一個閉包可以訪問到的變量裏,就可讓閉包訪問該對象了

var name2 =  "The  Window";
    var object2 = {
        name:"My Object",
        getNameFunc:function(){
            var that =  this; //將外部函數的this保存在外部函數的活動對象中(函數中申明的變量中)
            return function (){
                return that.name
            }
        }
    }
    
    console.log(object2.getNameFunc()())   //My Object

(三)閉包的缺點(1).因爲閉包會攜帶包含它的函數的做用域,所以會比其餘函數佔用更多的內存。過分使用閉包可能會致使內存佔用過多(2).閉包只能取得包含函數中任何變量的最後一個值,因此要注意寫法

相關文章
相關標籤/搜索