共计 1998 个字符,预计需要花费 5 分钟才能阅读完成。
概念:什么是闭包(Closure)
- MDN:闭包是函数和声明该函数的 ++ 词法环境 ++ 的组合。
A closure is the combination of a function and the lexical environment within which that function was declared. - 百度百科 1:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数)。
- 百度百科 2:闭包就是能够读取其他函数内部变量的函数,是“定义在一个函数内部的函数”。
- 其他说法 1:闭包是词法闭包的的简称, 是引用了 ++ 自由变量 ++ 的函数。
- 其他说法 2:闭包就是通过返回一个函数来保留某段作用域的一种方法,通过返回函数把本该消失的作用域保留到这个函数中。
可见:闭包并没有一个明确的、易于理解的概念,泛泛来看,闭包本质上是函数及作用域的一类问题。而实际上,我们也不太需要关注闭包的概念是什么,只需知道其表现、原理及常见的应用场景即可。
自由变量与作用域
- 作用域分:全局作用域、函数作用域和块作用域(ES6 后);
- 作用域是在函数定义时确定的,而不是在函数执行时确定的;
- 在 A 作用域中使用的变量 X,却没有在 A 中声明,那么对于 A 作用域来说,变量 X 即自由变量;
- 按照作用域链向上查找自由变量时,要先到创建该函数的作用域中去查找,而不是调用该函数的作用域中去查找;
var a = 10;
function sum(x) {return x + a;}
sum(1);
// 11
// 在函数 sum 的作用域里,a 就是自由变量
var a = 100;
function fn() {
var a = 10;
return function(x) {return a + x;}
}
var f = fn();
a = 200;
f(1);
// 11
// 自由变量 a 的值是沿着作用域链向上一级一级找到的
var x = 10;
function fn() {console.log(x);
}
function show(f) {
var x = 20;
(function() {f();
})()}
show(fn);
// 10
// 自由变量 x 在函数 fn 中执行,而 fn 在全局中定义
// 按上文总结:x 的值要到全局中(定义上下文)查找,而不是到函数 show 的作用域(执行上下文)中查找
var x = 10;
function show(y) {
var x = 20;
(function(z) {console.log(x+z)
})(y)
}
show(30);
// 30 + 20 = 50
闭包的表现、原因及其应用
- 闭包的两种表现:
// 函数作为返回值
function fn() {
var max = 10;
return function bar(x) {if (x > max) {console.log(x);
}
};
}
var f = fn();
f(20);
// 20
// 函数作为参数
var max = 10;
function bar(x) {if (x > max) {console.log(x);
}
}
(function(f) {
var max = 100;
f(15);
})(bar)
// 15
- 闭包产生的原因:
正常来讲,一个函数 fn 执行完成后,其上下文环境会销毁。但存在一种意外:
fn 执行完后返回了另一个函数 f,而正巧,返回的函数体中存在一个属于 fn 上下文环境的自由变量,那么 fn 执行完后,其上下文环境不能销毁。
很显然,闭包会增加内容开销。
- 闭包的实际应用:
首先需要知道每次调用外部函数,都会返回一个新的函数;而且返回函数的执行互不影响。
function lazy_sum(arr) {return function() {return arr.reduce((x,y) => {return x+y;})
}
}
var f1 = lazy_sum([1,2,3,4,5]);
var f2 = lazy_sum([1,2,3,4,5]);
f1 === f2; // false
以上便是闭包的一种应用:返回一个函数,并延迟执行。
闭包更强大的一个功能是:模拟面向对象的程序设计语言,封装一个 private 变量
// 使用 js 创建一个开放的计数器
function create_counter(initial) {
var x = initial || 0;
return {inc: function() {return ++x;}
}
}
var c1 = create_counter();
var c2 = create_counter(10);
c1.inc(); // 1
c1.inc(); // 2
c2.inc(); // 11
c2.inc(); // 12
c1.inc(); // 3
c2.inc(); // 13
闭包还可以把一个多参数的函数转化成单参数的函数:
function make_pow(n) {return function (x) {return Math.pow(x, n);
}
}
var pow2 = make_pow(2);
var pow3 = make_pow(3);
console.log(pow2(5)); // 5^2=25
console.log(pow3(7)); // 7^3=343
正文完
发表至: javascript
2019-09-04