1. 程式人生 > >HTML代碼中在兩個匿名函數中使用同名變量出現bug而引起的變量作用域的思考

HTML代碼中在兩個匿名函數中使用同名變量出現bug而引起的變量作用域的思考

資源管理 normal col gin image pac cor align 程序

  在學習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事件時則使用的是後面生成的變量。

HTML代碼中在兩個匿名函數中使用同名變量出現bug而引起的變量作用域的思考