function say() {
// Local variable that ends up within closure
var num = 888;
var sayAlert = function() { alert(num); }
num++;
return sayAlert;
}
var sayAlert = say();
sayAlert();//889
为什么最后的结果的889求解答
function say() {
// Local variable that ends up within closure
var num = 888;
var sayAlert = function() { alert(num); }
num++;
return sayAlert;
}
var sayAlert = say();
sayAlert();//889
为什么最后的结果的889求解答
1.为什么不是888var sayAlert = function() { alert(num); }
这句的意思是,创建一个函数,函数体是xx
,赋值给sayAlert
,所以alert
并没有执行。所以其在num++
之后执行。
2. 为什么可以访问 num
因为变量的访问看函数定义所在的位置,而不是调用的位置,详见闭包。
var sayAlert = say();
的时候num已经++了,变成了889
var sayAlert = function() { alert(num); }
这里的say()的内部变量num被外部引用了,所以常驻内存,没有被回收掉
这是js中的闭包,这方面知识还是有必要回去多看看理解理解的。还有就是js和其他语言一样,是在堆栈中运行的,say()函数执行的时候先压栈,因为num++;所以num的值变为889,之后sayAlert();执行,再压栈,打印输出
这是网上的闭包讲解可以看看
闭包
sayAlert 没有自己的局部变量,却可以访问 say 中的变量,这就是闭包在起作用,它是 JavaScript 中最基本的概念,但若要搞清它,得先深刻理解执行上下文,词法环境,作用域链这些概念,建议先查 MDN
13 回答12.9k 阅读
7 回答2.1k 阅读
5 回答1.4k 阅读
3 回答1.3k 阅读✓ 已解决
5 回答1.5k 阅读✓ 已解决
2 回答1.3k 阅读✓ 已解决
6 回答1.2k 阅读✓ 已解决
以下用比较容易理解的思路来说明。
JS中的函数会有个名称,另外还有自己的主体,这是我们拿来作函数定义时使用的,分别说明如下:
函数名称: 是拿来调用(执行、呼叫)的,这样才知道现在你是要调用哪个函数
函数主体: 这中是个块级(区块),用花括号({})括来起来,里面有一些语句,就是在调用了这个函数时要执行的代码
所以函数一经调用时,就会执行自己的主体中的代码,这应该都很好理解。
不过函数在"定义"与"调用",这是两码子事情,这要先在心里有个底,很多情况都会看到有不同之处。
接著,在一个函数主体中也可以定义其它新的函数,我们称它是"内部函数"或"巢状函数",例如"
上面的a变量,在调用了funcA函数后,它获得的是什么,是funcB的函数主体,也就是被赋值为funcB函数的执行代码内容,而且a变量成了一个函数类型。实验一下,你如果在浏览器主控台直接打印a变量,会得到下面的结果:
所以a变量也像函数一样可以调用,调用之后的结果就是打印出B。到这里也都很容易理解了。
接著讲闭包了,下面这个例子会比较简单些:
这个示例中的结果,打印出的a变量与b变量都是2,结果一样。闭包要理解得清楚,需要理解好函数的调用情况,以及当下的变量环境,这是什么意思呢?
刚前面已经有很重点的提醒,函数在"定义"与"调用"的情况是不一样的,所谓的不一样指的就是(变量)环境的改变。函数在定义情况的环境是这样,到了要被调用时,有可能被调用当下的变量有所不同,这样会造成函数调用的结果完全不同,尤其是函数主体中有用到外部的变量,外部变量指的是不在这个函数主体中所定义的变量。
以很简单的例子来说明,相信你一定看得懂:
上面的示例因为a变量改变了,而对funcA函数来说它在调用时会用到外部变量a,在调用后的结果要视变量a当下的值来决定结果,所以调用两次的结果是不同的。
这个设计是合理的,函数定义原本就只是集合多个执行语句的区块(块级)定义,调用时视调用当下的变量环境来决定这里面的执行语句该怎么执行,所以函数定义可以在代码文档中后面定义前面调用,这是JS语言与很多编程语言中都有的特色,这种特性称为"提升",不过这是另一回事了。
理解了这种函数"定义"与"调用"的概念,再回头来看原例子就不难了,但还有一个概念没清楚,就是这种变量环境会怎么运作,先来看原例子。
先看"说明1"的部份,在这个阶段,如同之前的示例一样,sayAlert变量给定的是由say函数所回传的sayAlert的函数主体,也就是得到
function() { alert(num); }
,并且sayAlert也是成了函数类型。在"说明2"调用了sayAlert变量所转变的函数主体,在这个情况下,它可能只有两种结果,一种就是888,另一个是有经过
++
的889。最终的结果是889而不是888,这是题主想要理解的部份。为何是经过加一的889?
原因首先是之前说的函数定义与调用是两个不同的期间,另一个重点是在于变量值被"记忆"了,这是一个很重要的关键,主要是刚说的内部函数(巢状函数),它本身有一种很特别的设计,它在返回时会"记忆"住在"创建"时的变量环境。
刚只有谈到函数在"定义"与"调用"的两个时期,这时又多了一个"创建"的时期,"创建"时期是一个特别的期间,最常见到的是在内部函数或IIFE样式时,会出现这种期间。实际上你直接在JS的全局中定义出函数,也算被创建了,只是内部函数会比较特别被指出来这个期间。例如下面的示例:
在
b = funcA()
这行语句执行时,返回的这个内部函数,这就"创建"出b函数,所以返回时除了它的函数主体外,额外也会"记忆"住返回时的变量环境,在这里指的就是在funcA函数主体中的a变量值,也就是a=1
这个变量值,所以每次当调用b函数(此时b变量已经变成一个函数,可被调用)时,就会将a变量作加一的处理,然后打印出来。这种设计称之为"闭包"(closure),比较好的理解是代表这些变量值被封闭到这个函数主体的环境中,当然这个函数仍然需要经过调用,才会执行其中的代码,不过这种函数中会带有在"创建"当下所"记忆"住的变量值。
所以,原题中的例子,在内部函数返回那个当下时(创建时),num变量会被"记忆"起来,也就是经过加一的889,所以最后在调用时,当然就是889了。