什么是闭包?
闭包:名词,指的是函数和引用环境
- <>
- 包含自由变量的函数与为所有这些自由变量提供了变量绑定的环境一起,被称为闭包
- 通过上述的书,我的理解是:一个内部函数总是可以访问其本身所在的外部函数中声明的变量和参数,即使在其外部函数被终结了之后.
闭包的用途是什么?
- 具有私密性
- 使用闭包后,只有通过专门的函数才能,才能拿访问读取函数内部的变量
- 让这些变量始终保持在内存中,不会被轻易删除
- 有利于代码的封装和使用
代码块
要理解闭包我们必须要了解两个个重要的概念,局部变量和全局变量
{let message = "Hello";alert(message); // 1.可以取到变量message}alert(message); // 2. 不可以取到变量message
- 代码1可以取到变量message
- 因为他和变量message是在同一个代码块中
- 这个变量message所能影响的范围也只在这个{ }花括号之间
- 代码2不可以取到变量message
- 因为这个变量message只在{ }花括号之间,也就是代码块之间
从上述的情况看变量message影响的范围是有限的,只在代码块之间,变量message就是局部变量
let messagemessage = "Hello";function f() {return alert(message); // 会显示"Hello"};
为什么会显示”Hello”?
doneMessage = ‘OUCH!’; // 此时变量是OUCH } setTimer(‘Cookies are done!’, 1000);
1. 想要获取内部函数f(),改怎样做呢?- 只要调用一下函数就可以了,不可以直接对函数f()修改,这样就有效的保证私密性2. 大家是否发现最后浏览器返回的是OUCH,而不是我们传进去的的参数Cookies are done?- **闭包包含的是实际环境,闭包函数外面的代码修改了变量,闭包函数执行时看到的将是变量的新值**- 当我们传参Cookies are done,刚刚进入函数的时候变量的确是Cookies are done,进一步代码运行触发setTimeout,计时器形成,代码继续执行此时变量就被替换了OUCH,故而最后返回的就是是OUCH<a name="fd8eb2e6"></a>### 变量始终保持在内存中```javascriptfunction makeCounter() { // 环境对象let count = 0; // 环境对象return function f() {return count++; // 环境对象};}let counter = makeCounter();counter(); // 0counter(); // 1counter(); // 2counter(); // 3
- 为什么最后的结果一直在不停的累计,而不是被删除掉呢?
- 注意: 在一个函数运行时,在调用刚开始时,会自动快速的创建一个新的环境对象以存储这个调用的局部变量和参数。
- 故而makeCounter会生成一个环境对象
let count = 0,会生成一个环境对象,在函数makeCounterreturn 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(); // 0counter(); // 1counter(); // 2counter(); // 3
在上述代码中
let count = 0中变量count,在每一次我们调用counter(),他的数值每一次都是会变化的,因为我们每一次修改的都是let count = 0这个环境对象的内容,那么变量count每次都会变化是肯定的.闭包的内存泄漏
全局作用域在页面关闭的时候销毁,函数作用域一般在代码执行完毕之后销毁。
但是闭包的情况又有所不同。外部函数的私有作用域(栈内存)被内部的函数占用着,所以外部函数在执行完毕后也不会销毁其私有作用域
function createComparisonFunction(propertyName) {return function(object1, object2) {var value1 = object1[propertyName];var value2 = object2[propertyName];if (value1 < value2) {return -1;}else if (value1 > value2) {return 1;}else {return 0;}}}var compare = creatComparisonFunction("name");var result = compare({ name: "Nichoals" }, { name: "Greg" });
手动解除引用
//=> 手动解除引用,以便释放内存//=> 释放内部函数的引用,那么内部函数会被销毁,那么外部函数执行时的私有作用域就没有被谁占用着,那么也会被销毁compare = null;
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存,过度使用可能会导致内存占用过多。使用闭包时,需要注意手动接触引用
