javascript - 关于闭包引申出的问题
高洛峰
高洛峰 2017-04-10 18:02:07
[JavaScript讨论组]

第一次代码

<body>
    <p class="p_area" id="1">1</p>
    <p class="p_area" id="2">2</p>
    <p class="p_area" id="3">3</p>
    <p class="p_area" id="4">4</p>
    <p class="p_area" id="5">5</p>

    <script>    
        var p=document.getElementsByClassName("p_area");
        for(var i=0;i<p.length;i++){
            p[i].onclick=function(){
                alert(p[i].id);
            }        
        }
       
    </script>
</body>

这段代码无法实现点击p输出p的id,因为i的值时5,但是我不明白的是为什么i的值是5,这是因为js没有块级作用域导致的还是闭包(看起来不是闭包啊,闭包最常见创建方式的不是函数内嵌函数吗,这里只有一个函数啊)

于是我把代码修改了以后为什么就又可以了?
第二次代码:

 alert(this.id);

接着我又把js代码改成了这样,创造了一个闭包,结果i的值也是5,所以DOM找不到,我想问造成i=5的原因和第一次的时候是否时一样的?

第三次代码:


<script>
    
        var p=document.getElementsByClassName("p_area");
        function add(){
            for(var i=0;i<p.length;i++){
                p[i].onclick=function(){
                    alert(p[i].id);
                }
        
            }
        }
    add();
       
    </script>
高洛峰
高洛峰

拥有18年软件开发和IT教学经验。曾任多家上市公司技术总监、架构师、项目经理、高级软件工程师等职务。 网络人气名人讲师,...

全部回复(6)
ringa_lee

本质上是一样的。

在执行alert(p[i].id);的时候,for循环已经完成,i的值已经变成5了。而this绑定的始终是被点击的对象。

巴扎黑

SegmentFault 上已经出现若干关于类似的装闭包问题了。
闭包一定要注意变量的作用域,因为你的闭包中一直都是用的同一个变量 i,而 i 是变化的,所以最好到使用的时候都是 i 的终值。

for (var i = 0; i < p.length; i++) {
    p[i].onclick = (function(n) {
        return function() {
            alert(p[n].id);
        }
    })(i);
}

其实可以不用闭包,因为在 onclick 函数里直接用 this 就是绑定事件的那个 p 啊。

for (var i = 0; i < p.length; i++) {
    p[i].onclick = function() {
        alert(this.id);
    }
}
黄舟

因为你onclick执行的时候,循环肯定早就执行完了,这时i为5。你可以试试改成立即执行的函数,是不是感觉正常了?

for(var i=0;i<3;i++){
    (function(){
        alert(i);
    })()
}
高洛峰

少年郎...建议你先去看《JavaScript 高级程序设计》第七章...你就懂啥是闭包了...

没有书也没关系...可以看别人归纳的读书笔记...比如我的~= ̄ω ̄=~

然后做一做常见的面试题...比如这篇

关于 this 指针的指向问题推荐看看《JavaScript秘密花园》

黄舟

这个就是闭包。

闭包不一定必须是两个函数嵌套,而是两个词法环境嵌套。只不过函数是最常见的可以形成词法环境的词法结构。

除了函数外,全局上下文、with语句、catch子句都可以形成词法结构,从而构成闭包。

ringa_lee

这个我来说说吧。
①因为你在for循环中。i 始终都是一个变量。在开始注册事件的时候,每个p对象的onclick属性都是一个函数,并且这个函数是

function(){
    alert(p[i].id)
}

,没错,这里是p[i].id,i的值还不确定。这里还没执行的。执行的条件就是你在浏览器点击了p,这时在内部循环已经完成该了。i=5。
②每个onclick事件执行时,this会绑定到点击它的dom元素。更详细的解释:每个p都是一个对象,onclick是它的属性,类似

p[i] = {
        onclick:function(){alert(this.id)}
}

,所以这个this隐式绑定到p[i]上。点击的时候会找到这个p的id属性。你可以把id的值换了试一试。
③和1是一样的。并不是闭包。闭包的概念:是在当前的作用域能使用其他作用域。这里可以使用一个即时函数(IIFE)。

var p=document.getElementsByClassName("p_area");
for(var i=0;i<p.length;i++){
    p[i].onclick=(function(count){
            return function(){
                alert(p[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上卷 没找到英文版,哪位找到发我一份啊。。谢谢啦!

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号