闭包
2024-01-17 19:43:22全局变量和局部变量
1. 全局变量
在任何地方都可以访问到的变量就是全局变量。
无论let、const还是var,在全局作用域下的都是全局变量。
不管在函数作用域还是块级作用域下,如果省略var、const、let定义的变量都是全局变量。
2. 局部变量
块级作用域下({},需要注意,在块级作用域下,通过var定义的变量是全局变量,let、const定义的变量是局部变量 )、函数等定义的变量,是局部变量。
什么是闭包
官方说法:闭包就是指有权访问另一个函数作用域中的变量的函数。 MDN说法:闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。
当执行函数A时,会创建一个函数执行上下文,并将函数执行上下文进入栈中,并创建函数中的作用域链、this、arguments等。在函数内部代码执行过程中,存在一个另一个函数B,并且这个函数B中存在对当前函数A中部分变量的引用。当函数A执行结束时,被函数B引用的变量并不会被垃圾回收(除非当函数B被垃圾回收),此时就形成了一个闭包。
function abc() {
const name = "GYQ";
function getName() {
console.log(name);
}
getName();
}
注意:MDN的定义不是很准确,ES6之后,产生了一个块级作用域。如果在块级作用中定义的let、const变量(被称为局部变量),如果在定义一个函数,并在函数中引用块级作用域中的变量,此时并不会产生一个闭包。
if (true) {
let abc = 123;
const ab = function() {
console.log(abc);
debugger;
}
ab();
}
闭包作用
- 使用闭包,我们可以访问到函数内部的变量,从而创建函数的私有变量。
function transition() {
const listeners = [];
return {
add(listener) {
listeners.push(listener);
return function() {
const index = listeners.indexOf(listener);
if (index > -1) {
listeners.splice(index, 1);
}
}
}
}
}
- 保存已经运行结束的函数内部的部分局部变量,使其不被释放(垃圾回收)。
使用场景
- IIFE(立即执行函数)
- 节流防抖
- 函数柯里化
- return 回一个函数
闭包经典面试题
const arr = [];
for(var i=0; i<5; i++) {
arr[i] = setTimeout(() => {
console.log(i); // 输出 5 5 5 5 5
}, 0);
}
修改后
const arr = [];
for(var i=0; i<5; i++) {
(function(i) {
arr[i] = setTimeout(() => {
console.log(i); // 输出 0 1 2 3 4
}, 0);
})(i);
}
闭包不会造成内存泄露
闭包造成内存泄露,是IE8及之前版本的bug。
在IE8以及更早版本的IE中,BOM和DOM对象并非是原生JavaScript对象,它是由C++实现的 组件对象模型对象(COM,Component Object Model),而COM对象使用引用计数算法来实现垃圾回收,所以即使浏览器使用的是标记清除算法,只要涉及到COM对象的循环引用,就还是无法被回收掉,就比如两个互相引用的DOM对象等等,而想要解决循环引用,需要将引用地址置为null 来切断变量与之前引用值的关系,如下:
function abc() {
let ele = document.getElementById("root");
let obj = {};
// 造成循环引用
obj.ele = ele;
ele.obj = obj;
// 切断引用关系
obj.ele = null;
ele.obj = null;
ele = null;
obj = null;
}
不过在IE9及以后的BOM与DOM对象都改成了JavaScript对象,也就避免了上面的问题。
内存泄漏是什么
是指由于疏忽或错误造成程序未能释放已经不再使用的内存
哪些情况会导致内存泄漏
以下四种情况会造成内存的泄漏:
隐式声明的全局变量
由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
setTimeout或者setInterval未能及时销毁
DOM对象的引用
获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。
闭包的不合理使用
不合理的使用闭包,从而导致某些变量一直被留在内存当中。