相关文章推荐
腹黑的香瓜  ·  Bulk Delete Users·  9 月前    · 
安静的回锅肉  ·  javascript - ...·  1 年前    · 

在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

function f1(){

var n=999;

function f2(){
alert(n);

return f2;

var result=f1();

result(); // 999

三、闭包的概念

上一节代码中的f2函数,就是闭包。

各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。

由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。

所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

四、闭包的用途

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

怎么来理解这句话呢?请看下面的代码。

function f1(){

var n=999;

nAdd=function(){n+=1}

function f2(){
alert(n);

return f2;

var result=f1();

result(); // 999

nAdd();

result(); // 1000

在这段代码中,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,可以在函数外部对函数内部的局部变量进行操作。

五、使用闭包的注意点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

六、思考题

如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。

代码片段一。

var name = "The Window";

var object = {
name : "My Object",

getNameFunc : function(){
return function(){
return this.name;

alert(object.getNameFunc()());

@tt 实际上后种方法每次调用 f1 时,都会声明 n = 999,而且 n 无法保留状态值(严格按照你的代码,其实 n 为全局变量,我理解你的本意为 var n = 999;)。

而第一种 f1 实际上返回的是个匿名函数,这样 n 作用域被另外个 f2 函数作用域所使用,因此它会保留。n 不会被重复声明,且内容会被保存

#1、有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

#2、这段代码中另一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

function f1(){
test = 10;
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
return f2;
//如果 #1 说法正确,下句会打印10,实际结果是test未定义。
//alert(test); // error test 未定义
//如果 #2 正确,语句 nAdd(); 位置在何处应该都能执行,测试结果在下面这个位置,也就是语句 var result=f1(); 前。是不能执行的。
//nAdd();
var result=f1();
result(); // 999
nAdd();
result(); // 1000

尝试解答代码段一:
getNameFunc: function() {//假设函数名为A
return function()/*假设函数名为B*/ { return this.name; };
在函数里面构建函数的时候,闭包产生。
在函数B内调用函数A的this.name,由于函数A没有name属性,所以就去找全局变量name,找到了,所以返回“The Window”,要是没有找到,则返回“undefined”。

代码段二可以尝试将代码更改为:
var _this = this;
return function() { return _this.name +"__"+ this.name; };
var tmp = Object.getNameFunc(); //此时没有执行this.name
var name = tmp();//这个时候才执行,这时候的this上下文为全局
alert(name);
//alert(object.getNameFunc()())

5 把例子2变成:
var name = "The Window";

var object = {
name : "My Object",

getNameFunc : function(){
var that = this;
return function(){
return that.name;
var tmp = Object.getNameFunc(); //这个时候执行了that = this,这里的this上下文是object,所以that指的是object
var name = Object.getNameFunc(); //这个时候执行了that.name
alert(name);
//alert(object.getNameFunc()());
alert(n);//弹出n的值是 1 (全局) 全局的函数 nAdd 和 由f1返回的函数中 所使用到的变量n全部为f1函数内的局部变量n,而不是全局变量n,证据就是上面的代码中最后一句 alert(n) 弹出的值是1, 也就是说 nAdd中的n++并没有改变全局变量中n的值.

你自己描述的是 f1()() 显示的是999,说明 n 是使用的f1内部的变量n,而非是全局变量n,不知道你为什么会有

说明nAdd中的n确实是作为全局变量存在。
这种想法呢? 如果想在nAdd中使用全局变量n(即在函数外面定义的n)的话,使用window.n来访问.

我感觉第一个思考题是不是这样理解:
首先this指向的是当前运行该函数的对象,
1、object.getNameFunc()得到了一个函数,函数为function(){return this.name}
2、object.getNameFunc()(),此时为window运行该函数,所以this指向的是window,所以this.name为The window

内部函数可以访问定义它们的外部函数的参数和变量(除了this和arguments之外)
如果需要访问对象的name属性的话,就需要显示的定义一个变量that来引用this,而这个变量此时就指向object对象了。

第一题改成下面这样就很清楚了。getNameFunc的第一个()是属于方法调用,所以this绑定到了object对象,自然this.name为"My Object",但是闭包函数无法访问这个this,它只能访问到全局的this。

var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
alert(this.name);
return function(){
return this.name;
alert(object.getNameFunc()());

1. var object = {。。。} 这是在干什么?是在声明一个变量?还是在声明一个类,然后里面有许多属性?

2 . object.getNameFunc()(); 怎么会有两个括号?

3. 如何判断 this指向的是object 对象还是全局对象 ?

object.getnameFunc() 在返回闭包函数前,将this赋给that,此时getnameFunc是由

object调用的,故而this指向object,当内部函数被返回时,由于闭包的特性,仍然

能访问到外部函数中的值,当执行打印My Object 。

代码段一、
当一个函数作为函数而不是方法调用的时候,这个this关键字引用全局对象。容易令人混淆的是,当一个嵌套的函数(作为函数)在一个包含的函数中调用,而这个包含的函数是作为方法调用的,这也是成立的:this关键字在包含的函数中有一个值,但是它却(不太直观地)引用嵌套的函数体的内部的全局对象。
所以第一个打印出来的是"The Window"
代码段二、
由于this关键字不是在包含的函数中引用的,而是通过that=this这个调用的,所以这个this不是在闭包内的,因此这个this就不能调用函数体内的全局对象,而是他的局部对象object.name,所以第二个打印出来的是"My Object"

引用tomwang的发言:
最后一个题感觉和闭包没什么关系啊,能详细解释一下吗?因为当一个函数作为函数而不是方法来调用的时候,this指向的是全局对象,这在《Javascript权威指南》上说的很清楚,所以答案肯定是“The Window”,和闭包没什么关系啊

完全赞同这位仁兄的看法!!前面闭包讲的不错,但是最后面的思考题出的很烂啊,跟闭包没关系,让人看完了反而糊涂了!

#1、有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

#2、这段代码中另一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

function f1(){
test = 10;
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
return f2;
//如果 #1 说法正确,下句会打印10,实际结果是test未定义。
//alert(test);// error test 未定义
//如果 #2 正确,语句 nAdd(); 位置在何处应该都能执行,测试结果在下面这个位置,也就是语句 var result=f1(); 前。是不能执行的。
//nAdd();
var result=f1();
result(); // 999
nAdd();
result(); // 1000

引用tomwang的发言:

最后一个题感觉和闭包没什么关系啊,能详细解释一下吗?因为当一个函数作为函数而不是方法来调用的时候,this指向的是全局对象,这在《Javascript权威指南》上说的很清楚,所以答案肯定是“The Window”,和闭包没什么关系啊

《Javascript权威指南》上说:如果嵌套函数作为函数调用,其this值不是全局对象(非严格模式下)就是undefined(严格模式下); 如果嵌套函数作为方法调用,其this值指向调用它的对象。所以代码片段一,getNameFunc()作为object的方法,所以this值应该指向调用它的对象(object),而object自己定义name为"My Object",所以片段一,我觉得应该是弹出"My Object"。我是新手,我只是从this方向对这个函数分析,有错误的地方,还望高手指点。

最后两个例子关键是区分object.getnameFunc()调用时和调用后的this指向。
第一个问题,object.getnameFunc()()函数在执行时this属于全局域,因此结果是the window
第二个问题,通过使用that=this 保留了在调用object.getnameFunc()时的this状态值。因此在getnameFunc()的闭包中访问的变量是已经不是this的了,而是that的~

思考题解答(个人见解):
1、因为闭包最后的返回值是一个函数,注意紧紧是一个函数而已 并没有执行,等到alert调用时才执行,而这时执行调用的方法,前面阮老师说了,函数内没有var声明的事全局变量,所以调用的getName是一个全局的变量,所以对应this找到的是全局的name.
2、因为this的对象引用在编译时就已经确定了,就是object,所以返回值在Object中开始查找,找到了Obeject中的name就不到全局变量中查找。

其实看完这篇文章,对于闭包的概念就有了更深刻的理解
最后留的两道题目,很好的说明了这一点。
因为第一题中getNameFunc这个方法或者叫函数是属于全局作用域的,所以里面返回的this始终都是指向window的。而第二题中用that=this改变了当前函数指向的作用域,所以第二题中的this最终只想的是myobject。
不知道我的理解正确不正确。
如果说错,请勿见怪...

不,我补充一下,我应该是说错了,既然f2函数是闭包,那么f2函数会保持它的外部函数f1的作用域,即使定义test为f1函数的局部变量(var test = 10);那么也要先检查f2是否对test产生引用,如果引用数为0,则在f1()调用完后销毁,再运行f1()()(实际上是调用f2函数)也不会产生对test这个局部变量的引用,所以它就在栈中被销毁了。
如果理解的不对,请大侠们拍砖

第一个 this为全局对象,所以alert处理的name为The window
第二个 that 为object对象,所以alert 处理的name为My object
第二个好理解,因为在调用前用that 保存了object 自己的this,所以that 就成为是有变量,在闭包内可以调用。
第一个有点晕,因为第一个的this指向自己,然后被window 给调用this 就成了window对象,它的name就是全局的name

引用陈守川的发言:

我觉得大家说了这么多,都没人把代码写出来运行一下来验证自己的答案么,也不管自己的答案是否正确么。通过运行发现:
思考题一:打印:空白
思考题二:打印:My Object
虽然我不太了解,但是评论中的很多人自己也理解错误了。