这是我参与更文挑战的第5天,活动详情查看:更文挑战
作为一个前端人,不管是在js学习之初还是在工作中都会遇到闭包,更甚者你已经在不经意间写了很多闭包。而闭包又是前端面试中几乎避不开的一个知识点。下面我们就来一起了解下闭包吧。
闭包的概念
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。《MDN》
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。《你不知道的javaScript》
上述两个文档中都很好的阐述了闭包的概念,我们可以知道闭包是一种现象,在js中由于有顶层对象的存在,可以说是每当创建一个函数的时候,闭包就会在函数创建的同时被一同创建出来。
闭包让你可以在一个内层函数中访问到其外层函数的作用域。
var windowName = '全局的name'
function makeFunc() {
var name = "Mozilla";
console.log(windowName)
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
复制代码
上述代码中,执行makeFunc函数将其内部定义的displayName函数作为返回值返回,并赋值给myFunc变量,最后执行myFunc,从而输出了定义在makeFunc函数内部的变量name的值Mozilla。我们可以看到displayName函数定义时是在makeFunc函数内部的,而最后执行时却是在window全局作用域中执行。并且成功输出了其所在的作用域中的name的值,这也就印证了《你不知道的javaScript》中对闭包的定义。
而以上代码起其实产生了两个闭包,其一是makeFunc和window之间形成的闭包,二便是displayName和makeFunc形成的闭包。
闭包的用法
看一个经典的栗子:
for (var i = 1; i <= 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
复制代码
相信很多人都知道上述代码会输出10个11,这是因为for循环是在全局环境中执行的,而setTimeout的执行环境也是全局环境,其内部输出的变量i自然也就是全局的,当setTimeout执行的时候for循环早已结束了,所以输出了10次11 那么为了达到预期的输出1~10就可以用到我们上面所说的闭包了。
for (var i = 1; i <= 10; i++) {
(function(i){
setTimeout(function () {
console.log(i);
}, 1000);
})(i)
}
复制代码
当然还可以使用let定义变量创建局部作用域从而解决这个问题
for (let i = 1; i <= 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
复制代码
闭包的作用
- 保护函数的私有变量不受外部的干扰。形成不销毁的栈内存。
- 保存,把一些函数内的值保存下来。闭包可以实现方法和属性的私有化。
PS: 闭包虽然保存了内部变量,但是由于引用关系一直存在,所以容易造成内存泄漏。