闭包

81 阅读2分钟

1. 什么是闭包

应用MDN文档来说:函数和对其周围状态(词法环境,lexical environment)的引用共同构成了闭包。也就是说,闭包可以让你在一个内层函数种访问到其外层函数的作用域。

如下代码就是一个闭包。

let x = 1
function f(){
    console.log(x)
}

2. 闭包的用途

  1. 从外部读取局部变量

    function f1(){
    	let n = 222
    	function f2(){
    		console.log(n)
    	}
    	return f2
    }
    let result = f1()
    result()     //222
    

    上面代码中,函数 f1 的返回值是函数 f2,由于 f2 可以读取 f1 内部变量,所以可以在外部获得 f1 的内部变量了。

  2. 让读取到变量的值始终保存在内存中

    function f1(){
    	let n = 222
    	function f2(){
    		console.log(++n)
    	}
    	return f2
    }
    let result = f1()
    result()     //223
    result()     //224
    result()     //225
    

    如上代码中,函数 f1 中的局部变量n一直保存在内存中,并没有在 f1 调用后被删除。原因在于 f1f2 的父函数, f2 被赋值给了一个全局变量,这使得 f2 一直存在在内存中,而 f2 的存在依赖于 f1 ,所以 f1 也一直存在内存中,不会被垃圾回收机制回收。

    所以,闭包也可以理解为能够读取其他函数内部的函数。

  3. 用闭包封装对象的私有属性和私有方法

    function Person(name){
        let $age
        function setAge(n){
            $age = n
        }
        function getAge(){
            return $age
        }
        
        return {
            name: name,
            getAge: getAge,
            setAge: setAge
        }
    }
    
    let p1 = Person('张三')
    p1.setAge(26)
    p1.getAge()    //26
    

上面代码中,函数 Person 的内部变量 $age,通过闭包 getAgesetAge,变成了返回对象 p1 的私有变量。

3. 闭包的优缺点

优点: 可以避免全局变量的污染。

缺点:

  1. 由于闭包会使得函数中的变量都被保存到内存中,内存消耗会很大。所以不能滥用闭包,否则会造成网页的性能问题,在 IE 中可能导致内存泄漏(解决方法是,推出函数前,将不适用的局部变量全部删除)。

  2. 闭包会在父函数外部,改变父函数内部变量的值。所以,在把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性时,一定不要随意改变父函数内部变量的值。