您的位置:首页 > Web前端 > JavaScript

javascript学习笔记(二)-闭包

2016-10-28 11:40 232 查看
闭包的理解和使用是学习js绕不过去的一道坎,为此笔者在阅读《javascript权威指南》的基础上又参考了几篇博客,终于了解个大概,下面就来和大家分享分享。

参考资料:

《javascript权威指南 第6版》

学习javascript闭包 - 阮一峰

MDN - JavaScript闭包

1. 变量作用域

要理解js中的闭包,就必须了解js的变量作用域。js的作用域有2种,全局变量和局部变量。

全局变量有全局作用域,一经定义就可在任何位置使用。这和静态语言的全局变量有些相似。而局部变量就有些特殊,不同于静态语言(如java、C)的
{}
作用域,javascript的作用域是函数范围的。

function test(){
//因为函数体内有定义a的地方,只是目前还未赋值,
//所以是undefined。
console.log(a);  //undefined
for (var i = 0; i < 5; i++) {
console.log(i); //0,1,2,3,4
var j=i;
}
var a = 1;
//由于js特殊的函数作用域,在for循环内部定义的
//i、j在循环结束后并没有销毁而是保存下来了
console.log('i='+i); // i=5
console.log('j='+j); // j=4

function inner(){
var inner1 = 123; //内函数的一个变量
// 特殊的函数作用域使得内部函数可以读取外部函数变量
console.log('a='+a);
console.log('i='+i);
}
inner(); // 输出 a=1 ,i=5
//console.log(inner1);
}
test();


也就是说,只要在一个函数体内定义的局部变量,在整个函数内都是有效的。所以上面例子中,内函数可以读取到外函数的变量,for循环结束后的变量还可以被读取。因为它们都属于同一个函数作用域(test()函数)内。

好了,现在思考这个问题。在内函数
inner()
定义的
inner1
这个变量外函数是否可以读取呢?答案是不能的。为什么,不是说函数内的变量都可以读取吗?

因为我们的inner()也是一个函数,正是由于函数作用域的存在,函数外的语句不能访问函数内的变量。所以到现在我们可以得知

javascript具有函数作用域

在嵌套函数中,嵌套函数可以访问外函数的变量

2.读取局部变量

上面我们说函数外是读取不到函数内定义的局部变量的。那我们有这个需求怎么办?

最简单的例子,我们想在函数外读取
test()
函数的
i
变量

function test(){
var i=0;
}
// 在此输出i的值


内函数可以读取局部变量,于是

function test(){
var i=0;
function inner(){
return i; //在内函数读取到了i值,并作为返回值返回
}
return inner();
}
console.log(test()); // 0


到此就解决了,可我们还有不满。现在是返回一个值,要是多个值呢?返回数组又不方便,还有没有别的方法?现在想,既然js可把函数当做返回值,能不能将内函数返回?

function test(){
var i=0;
function inner(){
return i;
}
return inner; //返回一个函数
}
var a = test(); //a是一个函数
console.log(a()); // 0


可行,至于为什么test()函数的局部变量
i
没有在test()执行完销毁而在调用a()时还能打印出来,这是下一篇深入闭包原理时讲的内容。现在我们先关注现象。

刚才说,要是函数内有多个局部变量,而要都在函数外访问怎么办?可以这样

function test() {
var v1 = 'v1的值';
var v2 = 'v2的值';

function funcV1() {
return v1;
}
function funcV2(){
return v2;
}
return {
//返回一个对象,并将内函数当做值传入
v1:funcV1,
v2:funcV2
}
}
var obj = test(); //接收对象值
console.log(obj.v1()); //v1的值
console.log(obj.v2()); //v2的值


这种写法有点眼熟,对了,在静态语言中(java、C#)读取类内的私有变量不和这很类似吗,在类里有get、set方法。类外调这些方法。不过在js中是读取函数内局部变量。

3.闭包

闭包广泛存在于函数式编程的语言中,维基百科上对闭包的定义是:

闭包是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

可以看出,闭包是一类函数,它能读取创建它时的局部变量(在js中就是内函数能读取外函数的局部变量),这不算稀奇,而稀奇的是,即使它离开了这个环境,它依然保存着这个变量。意味着,这个闭包保存了这些创建时引用的局部变量。

function test(){
var v1 = 1;
return function(){ //返回匿名的内函数
v1++;
return v1;
}
}
var f = test(); //现在f就是一个闭包函数,由于创建时引用了v1这个局部变量,现在它保存着这个变量
/**
* 每次调用,v1值加1
*/
console.log(f()); //2
console.log(f()); //3
console.log(f()); //4


上面解释的很清楚了,让我们再复杂一点,试着往闭包传参数。

function test(){
return function(a){
a.a1++;
}
}
var a ={
a1:1
}
var f = test();
//传入一个对象a
f(a);
f(a);
f(a);
//对象在闭包中被改变
console.log(a); // { a1: 4 }


总之,闭包就是访问了它的外部变量的函数,可以用它来模拟私有方法,封装局部变量等。至于其他的特性及原理日后有机会再研究。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息