第一次代码
<body>
<div class="div_area" id="1">1</div>
<div class="div_area" id="2">2</div>
<div class="div_area" id="3">3</div>
<div class="div_area" id="4">4</div>
<div class="div_area" id="5">5</div>
<script>
var div=document.getElementsByClassName("div_area");
for(var i=0;i<div.length;i++){
div[i].onclick=function(){
alert(div[i].id);
}
}
</script>
</body>
这段代码无法实现点击div输出div的id,因为i的值时5,但是我不明白的是为什么i的值是5,这是因为js没有块级作用域导致的还是闭包(看起来不是闭包啊,闭包最常见创建方式的不是函数内嵌函数吗,这里只有一个函数啊)
于是我把代码修改了以后为什么就又可以了?
第二次代码:
alert(this.id);
接着我又把js代码改成了这样,创造了一个闭包,结果i的值也是5,所以DOM找不到,我想问造成i=5的原因和第一次的时候是否时一样的?
第三次代码:
<script>
var div=document.getElementsByClassName("div_area");
function add(){
for(var i=0;i<div.length;i++){
div[i].onclick=function(){
alert(div[i].id);
}
}
}
add();
</script>
这个就是闭包。
闭包不一定必须是两个函数嵌套,而是两个词法环境嵌套。只不过函数是最常见的可以形成词法环境的词法结构。
除了函数外,全局上下文、with语句、catch子句都可以形成词法结构,从而构成闭包。
SegmentFault 上已经出现若干关于类似的装闭包问题了。
闭包一定要注意变量的作用域,因为你的闭包中一直都是用的同一个变量 i,而 i 是变化的,所以最好到使用的时候都是 i 的终值。
for (var i = 0; i < div.length; i++) {
div[i].onclick = (function(n) {
return function() {
alert(div[n].id);
}
})(i);
}
其实可以不用闭包,因为在 onclick 函数里直接用 this
就是绑定事件的那个 div 啊。
for (var i = 0; i < div.length; i++) {
div[i].onclick = function() {
alert(this.id);
}
}
因为你onclick执行的时候,循环肯定早就执行完了,这时i为5。你可以试试改成立即执行的函数,是不是感觉正常了?
for(var i=0;i<3;i++){
(function(){
alert(i);
})()
}
少年郎...建议你先去看《JavaScript 高级程序设计》第七章...你就懂啥是闭包了...
没有书也没关系...可以看别人归纳的读书笔记...比如我的~= ̄ω ̄=~
然后做一做常见的面试题...比如这篇
关于 this 指针的指向问题推荐看看《JavaScript秘密花园》
这个我来说说吧。
①因为你在for循环中。i 始终都是一个变量。在开始注册事件的时候,每个div对象的onclick属性都是一个函数,并且这个函数是
function(){
alert(div[i].id)
}
,没错,这里是div[i].id,i的值还不确定。这里还没执行的。执行的条件就是你在浏览器点击了div,这时在内部循环已经完成该了。i=5。
②每个onclick事件执行时,this会绑定到点击它的dom元素。更详细的解释:每个div都是一个对象,onclick是它的属性,类似
div[i] = {
onclick:function(){alert(this.id)}
}
,所以这个this隐式绑定到div[i]上。点击的时候会找到这个div的id属性。你可以把id的值换了试一试。
③和1是一样的。并不是闭包。闭包的概念:是在当前的作用域能使用其他作用域。这里可以使用一个即时函数(IIFE)。
var div=document.getElementsByClassName("div_area");
for(var i=0;i<div.length;i++){
div[i].onclick=(function(count){
return function(){
alert(div[count].id)
}
})(i)
}
如果还有什么不明白可以追问喔。答得不好的请多多包涵:)
----二次更新
一和三都不是闭包(个人看法)。如下代码
function foo(){
var a = 2;
function bar(){
console.log(a);
}
return bar;
}
var baz = foo();
baz();//2..
以上就是闭包。
解释如下:函数bar()的词法作用域能够访问foo()的内部作用域。然后将bar函数本身当作一个值类型进行传递。bar显然可以正常执行,在这个例子中,它在自己定义的词法作用域以外的地方执行。
在foo()执行后,通常会期待foo()的整个内部作用域被销毁(js的垃圾回收器)。
而闭包的"神奇"之处是可以阻止这件事情的发生。因为在bar()声明的地方,它拥有涵盖foo()内部作用域的闭包,使得该作用域能够一直存在,以供bar()在之后任何时间进行引用。
bar()依然持有对该作用域的引用,而这个引用就是闭包。
-----------------------------以下为书中原话(译版)
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前的词法作用域之外执行。下面用一些代码来解释这个定义。
function foo(){
var a = 2;
function bar(){
console.log(a);//2
}
bar();
}
foo();
这段代码看起来和嵌套作用域中的实例代码很相似。基于词法作用域的查找规则,函数bar()可以访问外部作用域的变量a(这个例子中的是一个RHS引用查询)。
这是闭包吗?
技术上来讲,也许是。但根据前面的定义,确切地说并不是。我认为最准确地用来解释bar()对a的引用的方法是词法作用域的查找规则,而这些规则只是闭包的一部分。(但却是非常重要的一部分!)
上面是书中原话。最后一段解释我感觉很模棱两可。可能是翻译的原因或者我没理解。
书名:你不知道的JavaScript上卷 没找到英文版,哪位找到发我一份啊。。谢谢啦!
本质上是一样的。
在执行alert(div[i].id);
的时候,for
循环已经完成,i
的值已经变成5了。而this
绑定的始终是被点击的对象。