JavaScript中闭包的写法和作用详解
转载 更新时间:2016年06月29日 10:01:35 作者:流浪的诗人
我要评论
width="700" height="60" scrolling="no" src="//pos.baidu.com/s?hei=60&wid=700&di=u3431600<u=https%3A%2F%2Fwww.jb51.net%2Farticle%2F87567.htm&ccd=24&ari=2&col=zh-CN&chi=1&pis=-1x-1&par=1366x728&dc=3&tcn=1529564562&prot=2&ti=JavaScript%E4%B8%AD%E9%97%AD%E5%8C%85%E7%9A%84%E5%86%99%E6%B3%95%E5%92%8C%E4%BD%9C%E7%94%A8%E8%AF%A6%E8%A7%A3_javascript%E6%8A%80%E5%B7%A7_%E8%84%9A%E6%9C%AC%E4%B9%8B%E5%AE%B6&cec=GBK&dri=0&dtm=HTML_POST&tpr=1529564562050&dai=2&psr=1366x768&cfv=0&tlm=1529564562&cja=false&ps=930x149&pss=1349x7406&exps=111000&pcs=1349x636<r=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DtFfFs1WNOZmd5wpJm2H8r76WEaUwKD8waiQeoYdkjM17WLctTwxalhm-lpoHh2GO%26wd%3D%26eqid%3D87df8f260007e95f000000065b2b4d8b&cdo=-1&cce=true&drs=1&cmi=5&ant=0&dis=0&cpl=4">
<div class="art_desc mt10"><div id="art_demo">本文给大家介绍javascript中的闭包,包括对js闭包概念的理解,闭包的几种写法和用法,闭包的主要作用,闭包与this对象,闭包与内存泄露及使用闭包的注意点相关知识,非常不错,具有参考借鉴价值,感兴趣的朋友一起学习吧</div></div>
<div class="lbd clearfix"><a href="http://www.php.cn/course.html?ad51" target="_blank"><img src="//files.jb51.net/image/phpcn_0508.gif" width="690" height="99"></a>
1、什么是闭包
闭包是有权访问另一个函数作用域的变量的函数。
简单的说,Javascript允许使用内部函数—即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。
2、变量的作用域
要理解闭包,首先要理解变量的作用域。
变量的作用域无非就是两种:全局变量和局部变量。
Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
其中内部函数中可以访问外部函数的变量,是因为内部函数的作用域链中包含了外部函数的作用域;
也可以理解为:内部函数的作用范围辐射到了外部函数的作用范围;
1
2
3
4
5
|
var
n=999;
function
f1(){
alert(n);
}
f1();
|
另一方面,在函数外部自然无法读取函数内的局部变量。
1
2
3
4
|
function
f1(){
var
n=999;
}
alert(n);
|
这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
1
2
3
4
5
|
function
f1(){
n=999;
}
f1();
alert(n);
|
3、闭包的几种写法和用法
3.1、给函数添加一些属性
1
2
3
4
5
6
7
8
9
|
function
Circle(r) {
this
.r = r;
}
Circle.PI = 3.14159;
Circle.prototype.area =
function
() {
return
Circle.PI *
this
.r *
this
.r;
}
var
c =
new
Circle(1.0);
alert(c.area());
|
3.2、是声明一个变量,将一个函数当作值赋给变量
1
2
3
4
5
6
7
8
9
10
11
|
var
Circle =
function
() {
var
obj =
new
Object();
obj.PI = 3.14159;
obj.area =
function
( r ) {
return
this
.PI * r * r;
}
return
obj;
}
var
c =
new
Circle();
alert( c.area( 1.0 ) );
|
3.3、这种方法使用较多,也最为方便。var obj = {}就是声明一个空的对象
1
2
3
4
5
6
7
|
var
Circle={
"PI"
:3.14159,
"area"
:
function
(r){
return
this
.PI * r * r;
}
};
alert( Circle.area(1.0) );
|
4、闭包的主要作用
闭包可以用在许多地方。它的最大用处有两个:一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
4.1、如何从外部读取局部变量?
出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数。
1
2
3
4
5
6
|
function
f1(){
var
n=999;
function
f2(){
alert(n);
}
}
|
在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
1
2
3
4
5
6
7
8
9
|
function
f1(){
var
n=999;
function
f2(){
alert(n);
}
return
f2;
}
var
result=f1();
result();
|
4.2、如何将变量的值始终保存在内存当中?
1
2
3
4
5
6
7
8
9
10
11
12
|
function
f1(){
var
n=999;
nAdd=
function
(){n+=1}
function
f2(){
alert(n);
}
return
f2;
}
var
result=f1();
result();
nAdd();
result();
|
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是”nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
5、闭包与this对象
在闭包中使用this对象可能会导致一些问题。因为匿名函数的执行具有全局性,因此其this对象通常指向window.代码如下:
1
2
3
4
5
6
7
8
9
10
|
var
name =
"The window"
;
var
object = {
name:
"My object"
,
getNameFun:
function
(){
return
function
(){
return
this
.name;
};
}
};
alert(object.getNameFun(){});
|
把外部作用域中的this对象保存在一个闭包能够访问的变量里面,就可以让闭包访问该对象了。代码如下:
1
2
3
4
5
6
7
8
9
10
11
|
var
name =
"The window"
;
var
object = {
name:
"My object"
,
getNameFun:
function
(){
var
that =
this
;
return
function
(){
return
that.name;
};
}
};
alert(object.getNameFun(){});
|
6、闭包与内存泄露
具体来说,如果闭包的作用域中保存着一个HTML元素,那么就意味着该元素无法被销毁。如下:
1
2
3
4
5
6
|
function
assignHandler(){
var
element = document.getElementById(
"someElement"
);
element.onclick =
function
(){
alert(element.id);
}
}
|
以上代码创建了一个作为element元素事件处理程序的闭包,而这个闭包则又创建了一个循环引用。由于匿名函数保存了一个对assignHandler()的活动对象的引用,因此会导致无法减少element的引用数。只要匿名函数存在,element的引用数至少也是1,因此他所占用的内存就不会被回收。
通过改写代码来解决内部不能回收问题:
1
2
3
4
5
6
7
8
|
function
assignHandler(){
var
element = document.getElementById(
"someElement"
);
var
id = element.id;
element.onclick =
function
(){
alert(id);
}
element =
null
;
}
|
以上代码,实现闭包不直接引用element,包含函数的活动对象中也仍然会保存一个引用。因此,有必要把element的变量设置为null,这样就可以正常回收其占用的内存。
7、使用闭包的注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
以上所述是小编给大家介绍的JavaScript中闭包的写法和作用详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!
<div class="art_xg">
您可能感兴趣的文章: