JavaScript闭包的理解

闭包(closure)是JavaScript语言的一个特点,要实现很多的高级功能都需要用到闭包。简单的说,闭包就是可以访问其他函数内部变量的函数。

1.变量的作用域

首先来了解一下JS里面变量的作用域,JS变量的作用域有两种,全局变量和局部变量,下面来了解一下他们可以在哪些地方被访问。

var n=123;
function al(){
    console.log(n);
}
al();
//输出为:
123

上面的例子创建了一个全局变量和一个函数,在函数内部访问全局变量,最后输出全局变量,说明在函数内部是可以访问全局变量的。

function al(){
    var n=123;
    console.log(n);
}
al();
console.log(n);
//输出为:
123
Uncaught ReferenceError: n is not defined

上面的例子在函数内部定义了一个局部变量,分别在函数内部和函数外部输出,结果现在在函数内部正常输出,在函数外部输出时报错。

还有一点需要注意,在函数内部声明局部变量的时候一定要加上var,否则将定义一个全局变量。如下:

function al(){
    n=123;
    console.log(n);
}
al();
console.log(n);
//输出为:
123
123

可以看出在函数内不使用var的时候n是一个全局变量,在JS中,不使用var声明就给变量赋值的情况下这个变量会变为全局变量。

2.如何在外部获取函数内部数据

很多时候我们需要在外部使用函数的内部数据,直接在外部使用函数内部的数据会报错,这时候就要使用闭包。
看一个例子:

function al(){
    var n=123;
    return function(){
        console.log(n);
    } 
}
var a=al();
a();
//输出为:
123

上面定义了一个名为al的函数,函数内部定义了一个变量n,返回了一个匿名函数,在匿名函数内部输出n,在外部调用al函数的内部函数时会输出在al内部定义的变量n。
这里我们需要了解一个概念,就是“作用域链”:

每一段js代码(全局代码或函数)都有一个与之关联的作用域链(scope chain)。

这个作用域链是一个对象列表或者链表,这组对象定义了这段代码中“作用域中”的变量。

当js需要查找变量x的值的时候,它会从链的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,如果第一个对象中没有名为x的属性,js会继续查找链上的下一个对象。如果第二个对象依然没有名为x的属性,则会继续查找下一个,以此类推。如果作用域链上没有任何一个对象含有属性x,那么就认为这段代码的作用域链上不存在x,并最终抛出一个引用错误(ReferenceError)异常。

简单的来说,就是子函数可以访问父函数里面的变量。例如在全局变量中定义的函数可以访问全局变量。

此时再看上面的例子,返回的匿名函数在al函数的里面,所以他可以访问al函数里面的变量,在外部使用这个匿名函数的时候就获取到了函数al里面的变量。

3.闭包的概念

关于闭包的概念网上都说的挺复杂,其实从一个代码中可以看出闭包就是在一个函数里面又嵌套并返回一个内部函数,在内部函数里面使用这个函数里面的变量。

4.闭包的用途

闭包的主要用途有两个:

  1. 调用函数的内部变量
  2. 将一个值永久的保存在内存中
    第一个在上面已经写了一个很简单的例子,怎么在用闭包在函数外部调用函数内部的值。
    第二种用途是将一个值永久的保存在内存中,下面来看一个例子
function al(){
    var count=1;
    return function(){
        console.log(++count);
    } 
}
var a=al();
a();
a();
a();
a();
//输出为:
2
3
4
5

从上面可以看出,函数al中的变量n一直存在内存中,每次对n进行操作的结果都会保存下来。

这里有一个很常见的使用闭包的题:有一个ul,如何在点击每列的时候alert他的index
一般下面会列出代码让你说明为什么不对:

//下面是ul的代码
<ul id="demo">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>
//下面是js的代码
var li=document.getElementById("demo").getElementsByTagName("li");
for(var i=0;i<li.length;i++){
    li[i].onclick=function(){
        alert(i);
    }
}

上面的js执行存在一个问题,就是不管点击哪一个列表,弹出的值都是4,这是因为页面加载的时候会先运行for循环,在点击列表的时候for循环已经运行完毕,i的值为4,所以每次点击都会显示4。
这里可以使用缓存i,或者闭包来解决这个问题,下面来写一个如何用闭包来解决这个问题,for循环可做如下修改:

for(var i=0;i<li.length;i++){
    li[i].onclick=(function(a){
        return function(){
            alert(a+1);
        }
    })(i);
}

这个解决方法用到了闭包和立即执行函数。

5.使用闭包的注意点

  1. 使用闭包会将函数中的变量一直保存在函数中,会一直占用内存,可能会造成内存泄漏,所以尽量不要大量的使用闭包,只在必须使用的时候才使用。
  2. 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,不要随便改变父函数内部变量的值。

如有错误请指正,谢谢!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值