什么是闭包?
闭包:名词,指的是函数和引用环境
- <<Head First JavaScript程序设计>>
- 包含自由变量的函数与为所有这些自由变量提供了变量绑定的环境一起,被称为闭包
- 通过上述的书,我的理解是:一个内部函数总是可以访问其本身所在的外部函数中声明的变量和参数,即使在其外部函数被终结了之后.
闭包的用途是什么?
- 具有私密性
- 使用闭包后,只有通过专门的函数才能,才能拿访问读取函数内部的变量
- 让这些变量始终保持在内存中,不会被轻易删除
- 有利于代码的封装和使用
代码块
要理解闭包我们必须要了解两个个重要的概念,局部变量和全局变量
{
let message = "Hello";
alert(message); // 1.可以取到变量message
}
alert(message); // 2. 不可以取到变量message
- 代码1可以取到变量message
- 因为他和变量message是在同一个代码块中
- 这个变量message所能影响的范围也只在这个{ }花括号之间
- 代码2不可以取到变量message
- 因为这个变量message只在{ }花括号之间,也就是代码块之间
- 从上述的情况看变量message影响的范围是有限的,只在代码块之间,变量message就是局部变量
let message
message = "Hello";
function f() {
return alert(message); // 会显示"Hello"
};
- 为什么会显示"Hello"?
- 当代码运行到alert(message),要使用变量message,会查找自己的代码块中,发现没有,就会继续往上查找发现变量message,这个变量就是全局变量
闭包具有私密性
function setTimer(doneMessage, n) { // 此时变量是Cookies are done
setTimeout(function f() {
alert(doneMessage);
}, n);
doneMessage = 'OUCH!'; // 此时变量是OUCH
}
setTimer('Cookies are done!', 1000);
- 想要获取内部函数f(),改怎样做呢?
- 只要调用一下函数就可以了,不可以直接对函数f()修改,这样就有效的保证私密性
- 大家是否发现最后浏览器返回的是OUCH,而不是我们传进去的的参数Cookies are done?
- 闭包包含的是实际环境,闭包函数外面的代码修改了变量,闭包函数执行时看到的将是变量的新值
- 当我们传参Cookies are done,刚刚进入函数的时候变量的确是Cookies are done,进一步代码运行触发setTimeout,计时器形成,代码继续执行此时变量就被替换了OUCH,故而最后返回的就是是OUCH
变量始终保持在内存中
function makeCounter() { // 环境对象
let count = 0; // 环境对象
return function f() {
return count++; // 环境对象
};
}
let counter = makeCounter();
counter(); // 0
counter(); // 1
counter(); // 2
counter(); // 3
- 为什么最后的结果一直在不停的累计,而不是被删除掉呢?
- 注意: 在一个函数运行时,在调用刚开始时,会自动快速的创建一个新的环境对象以存储这个调用的局部变量和参数。
- 故而makeCounter会生成一个环境对象
let count = 0
,会生成一个环境对象,在函数makeCounter
return count++
,也会生成一个环境对象
- 故而makeCounter会生成一个环境对象
- 当执行到
return count++
,会发现自身没有count变量,就往上一层找就发现了count
变量 - 请大家记住这句话 => 在哪里找到就在哪里修改,
count++
找到了count变量
,就会直接在这个环境对象中对变量count
进行修改,修改后的数据就直接储存在这个环境变量中 - 想必大家这个时候已经明白了,为什么每一次调用
counter();
,数值就会增加,因为每一次调用就是在变量count
自身的环境对象中已有的基础数据上不停增加修改,我们反复调用的就是同一个环境对象储存数据,并进行修改
闭包的缺点
- 因为闭包会使得函数中的变量都被保存在内存之中,使内存消耗加大
function makeCounter() { // 环境对象
let count = 0; // 环境对象,每一次都会变
return function f() {
return count++; // 环境对象
};
}
let counter = makeCounter();
counter(); // 0
counter(); // 1
counter(); // 2
counter(); // 3
- 在上述代码中
let count = 0
中变量count
,在每一次我们调用counter()
,他的数值每一次都是会变化的,因为我们每一次修改的都是let count = 0
这个环境对象的内容,那么变量count
每次都会变化是肯定的.