在了解闭包之前,我们先来了解下局部变量和全局变量
局部变量
即为定义在局部作用于下的变量,仅在这个作用域下被调用;
全局变量
即为定义在window下,在任何地方都可以调用。
那么他们有哪些优缺点呢?
局部变量用完会随着当前程序一同被销毁,不会占用内存,但是不可以重复使用。
全局变量可以重复使用,但是很容易造成变量污染。
看来局部变量和全局变量都有他的优缺点,
那么怎么样才能够变量重用,又可以保护变量不会被污染的一种机制呢?
答案当然是有的,这就是今天要介绍的内容 闭包
什么是闭包(closure)?
首先我们来看看分别在百度百科,菜鸟教程,W3Cschool和ECMAScript中对闭包的描述。
百度百科
闭包就是能够读取其他函数内部变量的函数。
菜鸟教程
闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。 直观的说就是形成一个不销毁的栈环境。
W3Cschool
JavaScript 变量可以是局部变量或全局变量。
私有变量可以用到闭包。
ECMAScript
闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。
综上所述,闭包就是外层函数的内层函数可以使用外层函数的变量。
怎么去理解这句话呢?
我们都知道,一般函数在执行完后会出栈,但是闭包导致作用域链不被释放,所有内层函数即使在外层函数执行完成后仍然可以访问外层函数里的变量。
function test(){
var a = 100;
return function (){
console.log(a);
}
}
var result = test();
result();
如上代码所示,代码在预编译
GO
存在test
和result
,test
指向function test(){...}
函数体,result
为undefined
,当函数test()
执行时,test
的AO
存在a
,执行后a
的值100
,在GO
中的result
赋值为function(){console.log(a)}
,此时test
出栈,但是因为test
内变量a
被result
所引用,所以test
的AO
无法被释放,result
可以访问到test
的AO
内的值,其中a
为受保护的变量。
闭包在开发中有什么作用?
在开发中我们会经常的使用动态添加点击事件,我们按照思维通常会写成下面这样
function test(){
var liList = document.getElementsByTagName("li");
for(var i = 0; i < liList.length;i++){
liList[i].onclick = function(){
console.log(i);
}
}
}
test()
但是因为我们在外层函数里使用了内层函数,并且使用了外层函数的变量i
,形成了闭包;
内层函数每一次循环都可以调用外层函数的值并且每次自增1
,而点击事件返回的函数体,此时并没有去执行;
当我们触发点击事件时,此时外层函数的i
为liList.length
,则每一次点击的i
为liList.length
,与我们预想的结果不同。
怎么去解决闭包所带来的问题?
闭包无非就是返回的函数内受保护的变量没有被放入返回的函数体内,只是返回了函数的内容,那么我们只需要让他返回具体的值。
function test(){
var liList = document.getElementsByTagName("li");
for(var i = 0; i < liList.length;i++){
(function(j){
liList[j].onclick = function(){
console.log(j);
}
}(i))
}
}
test()
如上代码所示,i
是变化的,但是只能返回i
,不能返回i
的值;我们定义一个匿名的函数,讲i
的值传入这个匿名函数,则每次返回的为console.log(1);console.log(2)......