一、闭包
闭包是指有权访问其他作用域中变量的函数。
1. 简单闭包
// 全局作用域
let num = 100;
// 声明一个函数
function fun() {
console.log(num++); // 100 101 102
}
// 调用函数
fun();
fun();
fun();
在函数 fun 中访问到全局作用域中的变量 num,这个函数就是闭包。
当函数 fun 第一次调用时,在函数作用域中没有变量 num,因此会沿着作用域链向上查询。
当函数 fun第一次执行的时候,将全局变量 num 由100变为101;当函数 fun第一次执行的时候,将全局变量 num 由101变为102;当函数 fun第一次执行的时候,将全局变量 num 由102变为103。
2. 调用函数的函数
嵌套声明的函数中,外层函数调用内层函数,内层函数使用了外层函数作用域内的变量,内层函数就是闭包。
// 声明的第一个函数(外层函数)
function outer(x) {
console.log("我是 outer 函数体,我被调用了");
// 声明第二个函数(内层函数)
function inner(y) {
console.log("我是 inner 函数体,我被调用了");
console.log(x + y); // 300
}
// 调用第二个函数
inner(200);
}
// 调用第一个函数
outer(100);
当 inner 函数执行的时候,inner 作用域中没有局部变量 x,因此沿着作用域链向上查询借用,函数 inner 就是闭包。
x = 100,y = 200,所以打印结果是300。
3. 返回函数的函数
一个函数返回另一个函数,返回的函数就是闭包。
function outer() {
// 声明一个局部变量
let a = 100;
// inner 函数的声明部分
function inner() {
a += 10;
return a;
}
return inner;
}
const result1 = outer();
console.log(result1()); // 110
console.log(result1()); // 120
const result2 = outer();
console.log(result2()); // 110
console.log(result2()); // 120
当 outer 函数第一次执行的时候,声明了一个局部变量 a = 100;以及声明了一个 inner 函数,并且把 inner 函数返回。
当 inner 执行时,在自身作用域中没有变量 a,因此沿着作用域链向上查询 outer。
二、作用域链(scope)
函数作用域(AO)和全局作用域(GO)的集合。
函数在被执行时,作用域链总第一个首先是自己的 AO,然后是父级 AO,最后是全局 GO。
function a() {
var aa = 123;
function b() {
var bb = 234;
console.log(aa); // 123
}
return b;
}
var res = a();
res(); //123
a 函数执行时 b 函数定义,此时 b 就已经能够访问到 a 函数的 AO。
三、闭包的两个意义
1. 延长变量的生命周期。如:防抖,节流等。
2. 创建私有环境。如:vue 中 data 是函数,计数器等。
私有作用域实例 - - 计数器
const makecounter = function() {
let num = 0;
let change = function(val) {
num += val;
}
return {
add: function() {
change(1);
},
reduce: function() {
change(-1);
},
value: function() {
return num;
}
}
}
const counter1 = makecounter(); // 0
const counter2 = makecounter(); // 0
counter1.add(); // 1
counter1.add(); // 2
counter2.add(); // 1
console.log(counter1.value()); // 2
console.log(counter2.value()); // 1