JavaScript 中的闭包
大多数 JavaScript 开发人员有意识或无意识地使用闭包。即使他们无意识地这样做,在大多数情况下也可以正常工作。但是知道闭包将在使用它们时更好地控制代码。学习闭包的另一个原因是它是 JavaScript 开发人员面试中最常见的问题。
先决条件:: JavaScript 中的变量作用域
让我们通过一个例子来看看和理解闭包。
示例 1:
Javascript
// Explanation of closure
/* 1 */ function foo()
/* 2 */ {
/* 3 */ var b = 1;
/* 4 */ function inner(){
/* 5 */ return b;
/* 6 */ }
/* 7 */ return inner;
/* 8 */ }
/* 9 */ var get_func_inner = foo();
/* 10 */ console.log(get_func_inner());
/* 11 */ console.log(get_func_inner());
/* 12 */ console.log(get_func_inner());
Javascript
/* 13 */ console.dir(get_func_inner);
Javascript
function foo(outer_arg) {
function inner(inner_arg) {
return outer_arg + inner_arg;
}
return inner;
}
var get_func_inner = foo(5);
console.log(get_func_inner(4));
console.log(get_func_inner(3));
Javascript
// Outer function
function outer()
{
var arr = [];
var i;
for (i = 0; i < 4; i++)
{
// storing anonymous function
arr[i] = function () { return i; }
}
// returning the array.
return arr;
}
var get_arr = outer();
console.log(get_arr[0]());
console.log(get_arr[1]());
console.log(get_arr[2]());
console.log(get_arr[3]());
Javascript
// Outer function
function outer()
{
function create_Closure(val)
{
return function()
{
return val;
}
}
var arr = [];
var i;
for (i = 0; i < 4; i++)
{
arr[i] = create_Closure(i);
}
return arr;
}
var get_arr = outer();
console.log(get_arr[0]());
console.log(get_arr[1]());
console.log(get_arr[2]());
console.log(get_arr[3]());
说明:这里要注意的是从第 9行到第 12 行。在第 9 行,我们完成了函数foo()的执行,并且由于第 7 行return inner ,函数inner()的整个主体被返回并存储在var get_func_inner中。
[return 语句不执行内部函数——函数仅在 () 后执行,而是 return 语句返回对函数的引用,因为 JavaScript 中的函数也是一个对象。]
我们可以通过函数inner()访问函数foo()中定义的变量b ,因为后者在执行封闭函数时保留了封闭函数的作用域链,即内部函数通过其作用域链知道b的值.
这是闭包,内部函数可以访问外部函数变量以及所有全局变量。
上述代码的输出:
为了查看闭包内绑定的变量和函数,我们可以写成:
Javascript
/* 13 */ console.dir(get_func_inner);
输出:
正如我们可以在范围部分看到闭包中的变量。
闭包的定义:
In programming languages, closures (also lexical closures or function closures) are techniques for implementing lexically scoped name binding in languages with first-class functions. Operationally, a closure is a record storing a function[a] together with an environment:[1] a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.[b]
-Wikipedia
或者
In other words, closure is created when a child function keep the environment of the parent scope even after the parent function has already executed
现在让我们看另一个例子。
示例 2:
Javascript
function foo(outer_arg) {
function inner(inner_arg) {
return outer_arg + inner_arg;
}
return inner;
}
var get_func_inner = foo(5);
console.log(get_func_inner(4));
console.log(get_func_inner(3));
说明:在上面的示例中,我们使用了参数函数而不是默认函数。请注意,即使我们完成了foo(5)的执行,我们也可以从内部函数访问outer_arg变量。并在执行内部函数时根据需要生成outer_arg和inner_arg的总和。
输出:
现在让我们看一个循环内的闭包示例。
在此示例中,我们将在数组的每个索引处存储一个匿名函数。
示例 3:
Javascript
// Outer function
function outer()
{
var arr = [];
var i;
for (i = 0; i < 4; i++)
{
// storing anonymous function
arr[i] = function () { return i; }
}
// returning the array.
return arr;
}
var get_arr = outer();
console.log(get_arr[0]());
console.log(get_arr[1]());
console.log(get_arr[2]());
console.log(get_arr[3]());
输出:
说明:你猜对了吗?在上面的代码中,我们创建了四个闭包,它们指向变量 i,它是函数外部的局部变量。闭包不记得变量的值,它只指向变量或存储变量的引用,因此返回当前值。在上面的代码中,当我们尝试更新它的值时,它会反映给所有人,因为闭包存储了引用。
让我们看看编写上述代码的正确方法,以便在不同索引处获得不同的 i 值。
示例 4:
Javascript
// Outer function
function outer()
{
function create_Closure(val)
{
return function()
{
return val;
}
}
var arr = [];
var i;
for (i = 0; i < 4; i++)
{
arr[i] = create_Closure(i);
}
return arr;
}
var get_arr = outer();
console.log(get_arr[0]());
console.log(get_arr[1]());
console.log(get_arr[2]());
console.log(get_arr[3]());
输出:
说明:在上面的代码中,我们每次调用都会更新函数create_Closure 的参数。因此,我们在不同的索引处得到不同的 i 值。
注意:一次获得闭包的概念可能有点困难,但请尝试在不同的场景中尝试闭包,例如创建 getter/setter、回调等。