定义
闭包是指那些能够访问自由变量的函数
什么是自由变量?
自由变量是指在函数中使用,但这个变量既不是函数参数,也不是函数局部变量
由此,闭包有两部分组成
闭包 = 函数 + 函数能够访问的自由变量
举个例子:
var a = 1;
function foo(){
console.log(a); // 1
}
foo();
foo 函数可以访问到变量a,变量a既不是foo函数的参数,也不是foo函数的局部变量,所以a是自由变量,形成了闭包。
《JavaScript权威指南》 中就讲到:从技术角度将,所有的JavaScript函数都是闭包。
ECMAScript中,闭包指的是:
1、从理论角度:所有的函数,在它们创建的时候将上层上下文的数据都保存起来,即使是全局变量也是如此,函数访问全局变量就相等于在访问自由变量,这时候使用全局(最外层)的作用域。
2、从实践角度:
i、即使创建它的上下文已经销毁,它仍然存在(如:父函数执行返回内部函数)
ii、在代码中引用了自有变量
分析
let scope = "global scope";
function checkscope(){
let scope = "local scope";
function f(){
return scope;
}
return f;
}
let foo = checkscope();
console.log(foo()); // local scope
由于作用域链,foo 函数依然可以读取到 checkscope.AO(AO表示checkscope函数作用域) 的值,即使 checkscope 执行被销毁了,JavaScript 依然会让 checkscope.AO 活在内存中,foo 函数依然可以通过作用域链找到变量 scope,从而实现了闭包这个概念。
面试必考题
let data = [];
for(var i = 0; i < 3; i ++){
data[i] = function(){
console.log(i);
}
}
data[0]();
data[1]();
data[2]();
答案都是 3。
先明确一下这两个概念:全局上下文:globalContext,GO表示全局作用域。AO表示函数局部作用域。
在执行到 data[0] 函数之前,此时全局上下文 GO 为:
globalContext = {
GO: {
data: [ ... ],
i: 3
}
}
当执行 data[0] 函数的时候,data [0] 函数的作用域链为:
data[0]Context = {
Scope: [AO, globalContext.GO]
}
data[0] 的 AO 并没有 i 值,所以会从 globalContext.GO中查找,此时全局 i 为 3,所以打印结果为3.
所以改成闭包将每次循环中的 i 的值,存储到 data数组中每个对应方法中。
let data = [];
for(var i = 0; i < 3; i ++){
data[i] = (function(i){
return function(){
console.log(i);
}
})(i)
}
data[0](); // 0
data[1](); // 1
data[2](); // 2
通过使用立即执行函数将 for 循环中全局 GO 的 此时的 i 值存储在在立即执行函数中参数,并在立即执行函数中返回一个函数,此时这个函数根据作用域链就可以访问立即执行函数 AO 中的 i 值。
当执行 data[0] 函数的时候,data[0] 函数的作用域链发生了变化:
data[0]Context = {
Scope: [AO, 匿名函数Context.AO globalContext.GO]
}
匿名函数执行上下文AO:
匿名函数Context = {
AO: {
i: 0
}
}
闭包的应用:
可通过立即执行函数实现属性私有化
闭包的防范:
闭包会导致多个执行函数公用一个共有变量,如果不是特殊需要,应尽量防止这种情况发生。
网友评论