function f1(){
n=999;
function f2(){
alert(n); // 999
}
}
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
function f1(){
n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
闭包就是能够读取其他函数内部变量的函数。
在JS中,作用域里没有以花括号包围的“块级作用域”概念,看一个例子:
if(true){
var a = 1;
}
console.log(a); //输出1
表面上看来, a在if语句里面定义着,那么console语句应该是访问不了的,然而事实恰恰相反,因为在JS中,if或者for语句的花括号,根本不是独立作用域,只有函数的花括号才起作用。
创建一个匿名函数并立刻执行
理论上讲,创建一个匿名函数并立刻执行可以这么写:
function (x) { return x * x } (3);
但是由于JavaScript语法解析的问题,会报SyntaxError错误,因此需要用括号把整个函数定义括起来:
(function (x) { return x * x }) (3);
变量的寿命兼闭包的第一大作用:延长寿命
不使用闭包:
var func = function(){
var a = 1; //退出后函数局部变量a直接被销毁
a++;
console.log(a);
};
func(); //2
func(); //2
func(); //依然是2
使用闭包:
var func = function(){
var a = 1;
return function(){ //匿名函数
a++;
console.log(a);
}
};
var f = func(); //f是对func()的引用
f(); //输出2
f(); //输出3
f(); //输出4
闭包的第二大作用:封闭变量
测试这段代码,就会发现无论点击那个div,最后弹出的结果都是5.这是因为div节点的onclick事件是被异步触发的,当事件被触发的时候,for循环早已经结束,所以i变量的值已经是5
<html>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<script>
var nodes = document.getElementsByTagName('div');
for(var i = 0, len = nodes.length; i < len; i++){
nodes[i].onclick = function(){
alert(i);
}
};
</script>
</body>
</html>
闭包解决:将每次循环的i值封闭起来, 当沿着作用域链从内到外查找变量i时,会先找到被封闭在闭包环境中的i
for(var i = 0, len = nodes.length; i < len; i++){
(function(i){
nodes[i].onclick = function(){
console.log(i);
}
})(i)
};
第三大作用:模拟面向对象
来看下面用面向对象实现的代码:
var extent = {
value:0,
call:function(){
this.value++;
console.log(this.value);
}
};
extent.call(); //输出:1
extent.call(); //输出:2
extent.call(); //输出:3
或者:
var Extent = function(){
this.value = 0;
};
Extent.prototype.call = function(){
this.value++;
console.log(this.value);
};
var extent = new Extent();
extent.call(); //输出:1
extent.call(); //输出:2
extent.call(); //输出:3
如果用闭包的方法,该怎么写呢?
var extent = function(){
var value = 0;
return {
call:function(){
value++;
console.log(value);
}
}
};
var extent = extent();
extent.call(); //输出:1
extent.call(); //输出:2
extent.call(); //输出:3
闭包还可以把多参数的函数变成单参数的函数。例如,要计算xy可以用Math.pow(x, y)函数,不过考虑到经常计算x2或x3,我们可以利用闭包创建新的函数pow2和pow3:
'use strict';
function make_pow(n) {
return function (x) {
return Math.pow(x, n);
}
}
// 创建两个新函数:
var pow2 = make_pow(2);
var pow3 = make_pow(3);
console.log(pow2(5)); // 25
console.log(pow3(7)); // 343
使用闭包的注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便
网友评论