HTML代码中在两个匿名函数中使用同名变量出现bug而引起的变量作用域的思考

  在学习HTML的时候,为了方便地对同一个css样式的不同值的效果进行对比,我做成了下面这个样子。

代码也是很典型的用于展示的格式(p元素的内容随便写的):

 1 <head>
 2     <style>
 3         p{
 4             border: medium solid black;
 5             padding: 5px;
 6             margin: 5px;
 7             text-justify:inter-word;
 8         }
 9         button{
10             margin: 5px;
11         }
12     </style>        
13 </head>
14 <body>
15     <p id="alignTest">
16         Visual Studio 使用模板创建项目。 C# .NET Core 控制台应用程序模板会自动定义类 
17         Program 和一个需要将 String 数组用作自变量的方法 Main。 Main 是应用程序入口点,
18         同时也是在应用程序启动时由运行时自动调用的方法。 args 数组中包含在应用程序启动时提供的所有命令行自变量。
19     </p>
20 
21     <button name="textAlign">start</button> <button name="textAlign">end</button> 
22     <button name="textAlign">left</button> <button name="textAlign">right</button> 
23     <button name="textAlign">center</button> <button name="textAlign">justify</button>
24 
25     <script>
26         var buttons=document.getElementsByName("textAlign");
27         var target=document.getElementById("alignTest");
28         for(var i=0;i<buttons.length;i++){
29             buttons[i].onclick=function(e){
30                 target.style.textAlign=e.target.innerHTML;
31             }
32         }
33     </script>
34 
35     <p id="white">
36         在“添加新项目”对话框中,展开“Visual C#”节点,并依次选择“.NET Standard”节点和“类库(.NET Standard)”项目模板。
37         在“名称”文本框中,输入项目名称“StringLibrary”。 选择“确定”,创建类库项目。然后,代码窗口在 Visual Studio 开发环境中打开。
38         请检查以确保库定目标到 .NET Standard 的正确版本。 右键单击“解决方案资源管理器”窗口中的库项目,再选择“属性”。 “目标框架”文本框显示定目标到 .NET Standard 2.0。
39     </p>
40     <div id="wh">
41         <button >normal</button ><button id="nor">nowrap</button><button>pre</button>
42         <button>pre-line</button><button>pre-wrap</button>
43     </div>
44     <script>
45         var whites=document.getElementById("wh").getElementsByTagName("button");
46         var target=document.getElementById("white");
47         for(var i=0;i<whites.length;i++){
48             whites[i].onclick=function(e){
49                 target.style.whiteSpace=e.target.innerHTML;
50             }
51         }
52     </script>
53 </body>

但当我在点击上面的按钮时,改变的确是下面的p元素的样式:

我仔细地检查了代码,我所声明的全部都是局部变量,那么问题出现在哪里呢?

然后我回想起了c#使用匿名函数的时候,由于闭包特性,变量会在调用才赋值,导致结果与期望不一致,然后我返回这段代码,把目光锁定到了下列代码上:

27         var target=document.getElementById("alignTest");
28         for(var i=0;i<buttons.length;i++){
29             buttons[i].onclick=function(e){
30                 target.style.textAlign=e.target.innerHTML;
31             }
32         }
46         var target=document.getElementById("white");
47         for(var i=0;i<whites.length;i++){
48             whites[i].onclick=function(e){
49                 target.style.whiteSpace=e.target.innerHTML;
50             }
51         }

我在这里声明了两个target变量,难道这个target也是在调用时才赋值?

于是我将下面的变量名target修改成了target1,最后代码如下:

<head>
    <style>
        p{
            border: medium solid black;
            padding: 5px;
            margin: 5px;
            text-justify:inter-word;
        }
        button{
            margin: 5px;
        }
    </style>        
</head>
<body>
    <p id="alignTest">
        Visual Studio 使用模板创建项目。 C# .NET Core 控制台应用程序模板会自动定义类 
        Program 和一个需要将 String 数组用作自变量的方法 Main。 Main 是应用程序入口点,
        同时也是在应用程序启动时由运行时自动调用的方法。 args 数组中包含在应用程序启动时提供的所有命令行自变量。
    </p>

    <button name="textAlign">start</button> <button name="textAlign">end</button> 
    <button name="textAlign">left</button> <button name="textAlign">right</button> 
    <button name="textAlign">center</button> <button name="textAlign">justify</button>

    <script>
        var buttons=document.getElementsByName("textAlign");
        var target=document.getElementById("alignTest");
        for(var i=0;i<buttons.length;i++){
            buttons[i].onclick=function(e){
                target.style.textAlign=e.target.innerHTML;
            }
        }
    </script>

    <p id="white">
        在“添加新项目”对话框中,展开“Visual C#”节点,并依次选择“.NET Standard”节点和“类库(.NET Standard)”项目模板。
        在“名称”文本框中,输入项目名称“StringLibrary”。 选择“确定”,创建类库项目。然后,代码窗口在 Visual Studio 开发环境中打开。
        请检查以确保库定目标到 .NET Standard 的正确版本。 右键单击“解决方案资源管理器”窗口中的库项目,再选择“属性”。 “目标框架”文本框显示定目标到 .NET Standard 2.0。
    </p>
    <div id="wh">
        <button >normal</button ><button id="nor">nowrap</button><button>pre</button>
        <button>pre-line</button><button>pre-wrap</button>
    </div>
    <script>
        var whites=document.getElementById("wh").getElementsByTagName("button");
        var target1=document.getElementById("white");
        for(var i=0;i<whites.length;i++){
            whites[i].onclick=function(e){
                target1.style.whiteSpace=e.target.innerHTML;
            }
        }
    </script>
</body>

终于在我点击了上面的按钮后更改的为第一个p的样式

由此我得知了在js中匿名函数的变量也是在调用时才赋值,但我不禁在想为什么我声明了两个局部变量后后面的局部变量会覆盖前边的局部变量,除非我声明的其实是同一个变量,即我在这里声明的变量的作用域与我之前接触的语言不一样的。

在查阅了资料后我得知了js没有块级作用域的特性和闭包特性。https://blog.csdn.net/u012896140/article/details/49494785

在我看来,没有块级作用域即代表着我所声明的target变量不像之前在c#声明的一样——不会有隐藏的特定前缀来指定该变量为独一无二的变量;而这一特性联合上闭包特性则使我之前声明的变量由于生存期被延长最后被第二个声明的target变量所替换,最后在调用上面button的onclick事件时则使用的是后面生成的变量。

猜你喜欢

转载自www.cnblogs.com/gokoururi/p/9686009.html