《javascript面向对象编程指南》——闭包

时间:2022-05-09 22:44:47

闭包:有权访问另一个函数作用域中的变量的函数,这样的话每个函数都可以被认为是一个闭包。但是大多数时候,该作用域在函数体执行完之后就自行销毁了

如果一个函数会在其父级函数返回之后留住对父级作用域链的话,相关闭包就会创建

首先是一个一般函数

var a = "global variable";
var F = function() {
    var b = "local variable";
    var N = function() {
        var c = "inner local";
    };
};

从全局到最里面的作用域链依次是:

  • a、F()
  • b、N()
  • c

现在将N的空间扩展到F以外,并止步于全局空间以内,就产生闭包

var a = "global variable";
var F = function() {
    var b = "local variable";
    var N = function() {
        var c = "inner local";
        return b;
    };
    return N;
};

var inner = F();
inner();    //"local variable"

如上,只需在F空间return出N空间,然后将F()的调用赋值给全局变量inner,此时inner就是N函数,调用一下inner(),就相当于执行N(),但是这里N(),既可以访问其原有的作用域,也是全局函数

上述步骤就是突破作用域链

下面这种闭包,不是F()返回函数,而是直接在函数体内N赋值给全局变量inner

var inner;
var F = function() {
    var b = "local variable";
    var N = function() {
        return b;
    };
    inner = N;
};

F();        //需要调用,才能将N赋值给inner
inner();    //"local variable"

相关定义与闭包

如果一个函数会在其父级函数返回之后留住对父级作用域链的话,相关闭包就会创建

function F(param) {
    var N = function() {
        return param;
    };
    param++;
    return N;
}

var inner = F(123);
inner();    //124

如上,当返回的函数被调用时,param++已经执行过一次递增操作了,所以inner()返回的是被更新后的值

由此,函数所绑定的是作用域本身,而不是在函数定义时该作用域中的变量或变量当前返回的值

循环中的闭包

如下,是一个三次循环操作,每次迭代中都会创建一个返回当前循环序号的新函数

function F() {
    var arr = [],
        i;
    for (i = 0; i < 3; i++) {
        arr[i] = function() {
            return i;
        };
    }
    return arr;
}

var newArr = F();
console.log(newArr[0]());   //3
console.log(newArr[1]());   //3
console.log(newArr[2]());   //3

如上,结果都是3

我们在这里创建了三个闭包,而他们都指向了一个共同的局部变量i。但是,如上节所说,闭包并不会记录他们的值,他们所拥有的只是相关域在创建时的一个连接(引用)

当循环结束时i的值为3,所以这三个函数都指向这一个共同值(注意不是2)

解决1

function F() {
    var arr = [],
        i;
    for (i = 0; i < 3; i++) {
        arr[i] = (function(x) {
            return function() {
                return x;
            };
        })(i);
    }
    return arr;
}

var newArr = F();
console.log(newArr[0]());
console.log(newArr[1]());
console.log(newArr[2]());

如上,将i传递给另一个即时函数

解决2

function F() {
    function binder(x) {
        return function() {
            return x;
        };
    }
    var arr = [],
        i;
    for (i = 0; i < 3; i++) {
        arr[i] = binder(i);
    }
    return arr;
}

var newArr = F();
console.log(newArr[0]());
console.log(newArr[1]());
console.log(newArr[2]());

如上,定义一个内部函数,将i本地化