闭包补充练习

案例:出现产品条,当单击第几行产品,出现对应编号在这里插入图片描述

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>JavaScript闭包属性详解</title>
    <style type="text/css">
        p {
            background: gold;
        }
    </style>
    <script type="text/javascript">
        function init() {
            var pAry = document.getElementsByTagName('p');
            for (var i = 0; i < pAry.length; i++) {
                pAry[i].onclick = function() {
                    alert(i);
                }
            }
        }
    </script>
</head>

<body onload="init();">
    <p>产品0</p>
    <p>产品1</p>
    <p>产品2</p>
    <p>产品3</p>
    <p>产品4</p>
</body>

</html>
  • 问题:无论单击哪个div,都会弹出5。

  • 原因:onclick事件是异步触发的,当事件被触发时,for循环早已结束,此时变量i的值早已经是5。

  • 解决:在闭包的帮助下,把每次循环的i值都封闭起来。当事件函数顺着作用域链从内到外查找变量i时,会先找到被封闭在闭包环境的i,单击div时,会分别输出0,1,2,3,4。

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>JavaScript闭包属性详解</title>
    <style type="text/css">
        p {
            background: gold;
        }
    </style>
    <script type="text/javascript">
        function init() {
            var pAry = document.getElementsByTagName('p');


  


            for (var i = 0; i < pAry.length; i++) {

                function outer(n) {
                    var m = n;
                    return function inner() {

                        alert(m);

                    }
                }
                pAry[i].onclick = outer(i);
                


            }
        }
    </script>
</head>

<body onload="init();">
    <p>产品0</p>
    <p>产品1</p>
    <p>产品2</p>
    <p>产品3</p>
    <p>产品4</p>
</body>

</html>

更经典和简单的解决办法是使用立即执行函数。

在这里插入图片描述
为什么这样就行了呢,function()自己调用了自己,这时候onclick引用的变量变成了n,而由于立即执行函数的原因,每个onclick函数在作用域链中分别保持着对应的n(0~length-1),这时候就可以了。

补充:立即执行函数
立即执行函数案例

(function(a, b){ // 形参 
  console.log(a + b); // 3 
}(1, 2)); // 实参

此题对应

(function(n){//n是形参
                    pAry[n].onclick = function() {
                        alert(n);
                    }
                })(i);//i是实参

所以利用立即执行函数,来实现外部调用内部变量。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>JavaScript闭包属性详解</title>
    <style type="text/css">
        p {
            background: gold;
        }
    </style>
    <script type="text/javascript">
        function init() {
            var pAry = document.getElementsByTagName('p');
            for(var i=0; i<pAry.length; i++) {
                (function(n){
                    pAry[n].onclick = function() {
                        alert(n);
                    }
                })(i);
            }
        }
    </script>
</head>
<body onload="init();">
<p>产品0</p>
<p>产品1</p>
<p>产品2</p>
<p>产品3</p>
<p>产品4</p>
</body>
</html>

运行后,在网页下按F12中。打开source选项
在第14行前面点击添加断点,可以观察到作用域链

代码在第14行和15行之间来回跳转,因为没有单击事件发生,所以没有alert(i)事件发生。
直到i=5,init()函数执行完成。
在这里插入图片描述

最简单的方法:把var改成let

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>JavaScript闭包属性详解</title>
    <style type="text/css">
        p {
            background: gold;
        }
    </style>
    <script type="text/javascript">
        function init() {
            var pAry = document.getElementsByTagName('p');

            for (let i = 0; i < pAry.length; i++) {

                pAry[i].onclick = function() {
                    alert(i);
                }

            }
        }
    </script>
</head>

<body onload="init();">
    <p>产品0</p>
    <p>产品1</p>
    <p>产品2</p>
    <p>产品3</p>
    <p>产品4</p>
</body>

</html>
posted @ 2022-04-06 23:52  szmtjs10  阅读(12)  评论(0编辑  收藏  举报