[JavaScript] 闭包
在JavaScript中存在着闭包这以概念,也是前端面试中经常会提到的一个知识点,下面就来介绍一下闭包吧。
闭包是什么?
首先来看下MDN(Mozilla Developer Network)官网对于闭包这一概念的定义
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。——MDN官网(相关链接)
读起来不太好理解,实际上翻译成白话文就是:在一个作用域中可以访问另一个函数内部的局部变量的函数。
下面是闭包的一个基本使用
javascript
复制代码
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
可以发现在displayName这个作用域下访问了另外一个函数makeFunc下的局部变量name
闭包的实现,实际上是利用了JavaScript中作用域链的概念,简单理解就是:在JavaScript中,如果在某个作用域下访问某个变量的时候,如果不存在,就一直向外层寻找,直到在全局作用域下找到对应的变量为止,这里就形成了所谓的作用域链。
闭包的特性
- 闭包可以访问到父级函数的变量
- 访问到父级函数的变量不会销毁
现在来看下闭包的相关应用,首先来看下下面这段代码:
javascript
复制代码
var age = 18;
function person(){
age++;
console.log(age);
}
person(); // 19
person(); // 20
person(); // 21
可以看到这里调用了3次函数,age的值也从18增长到了21,但是这么写会导致全局变量被污染,所以将age的定义移动到person函数内部,代码如下:
javascript
复制代码
function person() {
var age = 18;
age++;
console.log(age);
}
person(); // 19
person(); // 19
person(); // 19
但是这又导致了另一个问题,变为局部变量的age不会自增了,所以那么就可以利用闭包的这个特性将每次调用时的age保存起来这样就可以实现变量的自增了,代码如下:
javascript
复制代码
function person() {
var age = 18;
return function(){
age++;
console.log(age);
}
}
let getPersonAge = person();
getPersonAge(); // 19
getPersonAge(); // 20
getPersonAge(); // 21
可以这样理解,通过将person函数赋值给getPersonAge这个变量,可以看作如下代码
javascript
复制代码
let getPersonAge = function(){
age++;
console.log(age);
}
每当调用getPersonAge()函数的时候,首先要获取age变量,因为JavaScript中存在作用域链的关系,所以会从person函数下得到对应的age,因为闭包存在着闭包可以访问到父级函数的变量,且该变量不会销毁的特性所以上次的变量会被保留下来,所以可以做到自增的实现。
如果对变量不会销毁这一特性有疑问可以参考下寸志老师对于闭包的理解:
函数当作值传递,即所谓的first class对象。就是可以把函数当作一个值来赋值,当作参数传给别的函数,也可以把函数当作一个值 return。一个函数被当作值返回时,也就相当于返回了一个通道,这个通道可以访问这个函数词法作用域中的变量,即函数所需要的数据结构保存了下来,数据结构中的值在外层函数执行时创建,外层函数执行完毕时理因销毁,但由于内部函数作为值返回出去,这些值得以保存下来。而且无法直接访问,必须通过返回的函数。这也就是私有性。
作者:寸志 链接:www.zhihu.com/question/34…
闭包的应用
所以就可以根据这个特性做几个小案例测试一下。
循环注册事件
比如就可以利用闭包的特性做循环点击事件,比如下面的给输入框添加onblur事件:
需求:点击输入框,上面的提示栏显示对应的内容