03 JS 闭包

1.概念

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。

2.示例说明

有关如何创建作用域链以及作用域链有什么作用的细节,对彻底理解闭包至关重要。

// 代码示例 1
function createComparisonFunction(propertyName) {
    return function(object1, object2) {
        let value1 = object1[propertyName];
        let value2 = object2[propertyName];
        
        if(value1 < value2) {
            return -1;
        } else if(value1 > value2) {
            return 1;
        } else {
            return 0;
        }
    }
}

// 创建函数
let compareNames = createComparisonFunction('name');
// undefined
// 调用函数
let result = compareNames({name: 'Zhang'}, {name: 'Li'});

// 调用函数
let out = compareNames({name: 'Zhang'}, {name: 'Li'});
// undefined
out;
// 1

分析上述示例 1 代码:

在匿名函数从 createComparisonFunction() 函数中被返回后,它的作用域链被初始化为包含 createComparisonFunction() 函数的活动对象和全局变量对象。createComparisonFunction() 函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。换句话说,当 createComparisonFunction() 函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然会留在内存中,直到匿名函数被销毁后,createComparisonFunction() 的活动对象才会被销毁。

闭包

由于闭包会携带包含其它的函数的作用域,因此会比其它函数占用更多的内存。 慎重使用闭包。

3.闭包与变量

作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。闭包所保存的是整个变量对象,而不是某个特殊的变量。

// 代码示例 2
function createFunction() {
    let result = new Array();

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

上述代码示例 2 中,每个匿名函数的作用域链中都保存着 createFunction() 函数的活动对象,所以它们引用的都是同一个 i。当 createFunction() 函数返回后,变量 i 的值是 10,而每个函数都引用着保存变量 i 的同一个变量对象,每个函数内部 i 的值都是 10。

// 代码示例 3
function createFunction() {
    let result = new Array();

    for(let i = 0; i < 10; i++) {
        result[i] = function(num) {
            return function() {
                return num;
            }
        }(i);
    }
    return result;
}

代码示例 3 中,通过创建另一个匿名函数强制让闭包的行为符合预期。在调用每个匿名函数时,我们传入了变量 i,由于函数参数是按值传递的,所以就会将变量 i 的当前值复制给参数 num。而这个匿名函数内部,又创建并返回了 num 的闭包。这样一来, result 数组中的每个函数都有自己 num 变量的一个副本,因此可以返回各自不同的副本。

总结

闭包,就是在一个函数内部创建另一个函数。里面函数的作用域链中引用了外部函数的活动对象,外部函数调用执行完成时,外部函数的作用域链会销毁,而因为里面函数的作用域链中引用了外部函数的活动对象,只有在里面函数的执行环境销毁后,外部函数的活动对象才你销毁。使用过多闭包会使内存占用过多。

对于闭包的情况,闭包函数只有获取包含函数中任何变量的最后一个值。我们可以通过在匿名函数再次将闭包函数包裹起来,而闭包参数的引用通过匿名函数引用包含函数的值传递给参数。

注:文章参考总结自 《JavaScript 高级程序设计》(第 3 版)[美] Nicholas C.Zakas 著 第 178 页。

你可能感兴趣的:(03 JS 闭包)