javascript的變數作用域--對比js、php和c的for迴圈

来源:http://www.cnblogs.com/yangtoude/archive/2016/11/20/6083131.html
-Advertisement-
Play Games

通過對比js、php、c三門語言的for迴圈,加強自己對js變數作用域和php for迴圈的理解。 ...


為什麼要寫這篇文章呢?主要是給自己提個醒,js的水很深,需要小心點兒才能趟過去,更何況自己不是專業人士,那就得更加小心了。

看下麵的js代碼:

 1 <!DOCTYPE html>
 2 <html>
 3   <head>
 4     <meta charset="utf-8">
 5     <title>javascript中變數的作用域</title>
 6   </head>
 7   <body>
 8 
 9   <script>
10   for (var i = 0; i < 3; i++) {
11     console.log(i);
12   }
13   console.log('第一個for迴圈結束');
14   console.log(i);
15 
16   console.log('第二個for迴圈開始');
17 
18   for (; i > 0; i--) {
19     console.log(i);
20   }
21     console.log('第二個for迴圈結束');
22   console.log(i);
23   </script>
24   </body>
25 </html>

你猜它的輸出是什麼?

為何迴圈結束後,i的值仍然存在呢?js迴圈代碼塊內部變數的作用域怎麼跑到外面來了?作為js小白的我,竟然會問這麼傻的問題。答案是,js沒有塊級作用域,像for、if、switch等控制結構形成的代碼塊內部聲明的變數其實都是全局變數。對於像c這種有塊級作用域的語言,在for語句執行完畢後,迴圈變數會被銷毀,下麵是c語言的for迴圈:

1 #include "stdio.h"
2 
3 int main() {
4     for (int i = 0; i < 3; i++) {
5         printf("%d\n", i);
6     }
7 
8     printf("%d\n", i); // 這裡編譯時會報錯 error: use of undeclared identifier 'i'
9 }

編譯時報錯如下:

而對於javascript這種沒有塊級作用域的語言,即使在for迴圈結束之後,迴圈變數i也是會存在於迴圈外部的“全局”當中,因為這個迴圈變數其實就是“全局變數”。這種描述不夠準確,javascript當中一個重要的概念是執行環境(execution context),簡稱環境。每個環境都有一個與之關聯的變數對象,環境中定義的所有變數和函數都保存在這個對象中。全局執行環境是最外圍的一個執行環境,比如在瀏覽器中,全局執行環境就是window對象,所以,所有的全局變數都是作為window對象的屬性和方法創建的。比如上面的迴圈變數i,應該可以訪問window.i:

下麵只寫出js代碼:

 1   for (var i = 0; i < 3; i++) {
 2     console.log(i);
 3   }
 4   console.log('第一個for迴圈結束');
 5   console.log(i);
 6   console.log('訪問window.i');
 7   console.log(window.i);
 8   
 9   console.log('第二個for迴圈開始');
10 
11   for (; i > 0; i--) {
12     console.log(i);
13   }
14     console.log('第二個for迴圈結束');
15   console.log(i);
16 
17   console.log('訪問window.i');
18   console.log(window.i);

結果如下:

那麼,javascript中的局部變數該怎麼定義呢?(或者說如何定義一個變數,這個變數不會被影響到全局呢),答案是函數。js中除了全局執行環境外,還有一個執行環境:函數執行環境,也可以把它叫做局部執行環境。比如下麵這段代碼:

1   var j = 0;
2   function test() {
3     var j = 1;
4     return j;
5   }
6 
7   console.log(j);

輸出結果為0,證明函數內部的j沒有污染到外部的j,函數內部的j只在函數的執行環境內被訪問。

我又想到php跟js一樣都是腳本語言,那麼php是不是也沒有塊級作用域呢?看下麵的代碼:

 1 <?php
 2 
 3 if (true) {
 4     $i = 7;
 5 }
 6 echo $i , PHP_EOL;
 7 
 8 for ($i = 0; $i < 3; $i++) {
 9     echo $i , PHP_EOL;
10 }
11 echo '第一個for迴圈結束' , PHP_EOL;
12 echo $i , PHP_EOL;
13 
14 echo '第二個for迴圈開始' , PHP_EOL;
15 for (; $i > 0; $i--) {
16     echo $i , PHP_EOL;
17 }
18 echo '第二個for迴圈結束' , PHP_EOL;
19 
20 echo $i , PHP_EOL;

輸出結果如下:

嗯,沒錯,它也沒有塊級作用域。其實php手冊中很“含蓄”的表達了這一點:“變數的範圍即它定義的上下文背景(也就是它的生效範圍)。大部分的 PHP 變數只有一個單獨的範圍。這個單獨的範圍跨度同樣包含了 include 和 require 引入的文件。......在用戶自定義函數中,一個局部函數範圍將被引入。任何用於函數內部的變數按預設情況將被限制在局部函數範圍內。”

我又想到了php的for迴圈,我記得php手冊中對於foreach迴圈的描述中有這麼一段話“Warning:數組最後一個元素的 $value 引用在 foreach 迴圈之後仍會保留。建議使用 unset() 來將其銷毀。”,這個恐怕也是因為它沒有塊級作用域的原因,但手冊中for迴圈中沒有提到這一點,這有些奇怪。看來,php的水也很深。

其實,javascript的ES6規範中已經引入了let(還有const)變數標識符,利用let可以讓js的變數擁有塊級作用域,看下麵的代碼:

1   for (let i = 0; i < 3; i++) {
2     console.log(i);
3   }
4   console.log('第一個for迴圈結束');
5   console.log(i);

輸出結果如下:

看來js正在變得越來越規範。

 

ps:js的執行環境、php的局部變數和全局變數,以後還要仔細學習下。

 

參考:

1,javascript高級程式設計

2,php手冊


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 昨天用for迴圈進行數組去重的時候出現的問題, 首先,用雙重for迴圈把前一個和所有後面的元素進行比較,如果相等則刪除。 但是,如果數組裡面有三個以上連續相等的元素的時候,就會出現問題。 輸出: 這是因為當數組刪除一個元素的時候,數組長度減1,後面點元素就會往前移動一位,索引也減1,但是j還是進行了 ...
  • 前言 經常會有人問我如何才能將自己做的靜態頁面放到網上供他人欣賞,是不是需要自己有一個伺服器,是不是還要搞個功能變數名稱才能訪問?對於以上問題我都會回答:用github來展示你的前端頁面吧。 工欲善其事,必先利其器。github是一個很好的代碼管理與協同開發平臺,在程式界又被稱為最大的“同性交友網站”。如果 ...
  • 很好用的一款插件jQuery+turn.js翻書、文檔和雜誌3種特效演示 線上預覽 下載地址 實例代碼 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xh ...
  • 一、左右欄寬度固定,中間欄寬度自適應 左右欄寬度固定,中間欄寬度自適應 main left right 縮小視窗的效果: 二、除去列表右邊框 除去列表右邊框 除去列表右邊框 除去列表右邊框 除去列表右邊框 除去列表右... ...
  • 響亮的標題:一個萬能的,保底的。面向過程改寫成面向對象的方法 前提朗讀:很多剛接觸js面向對象的時候都不知道如何能快速的寫出一個面向對象的程式,這個是必然的現象,不是每一位學js的一上來就會寫面向對象的程式。小編這裡介紹的方法屬於一個面向過程到面向對象的過度期間,(這裡要明白麵向對象的一些常識:構造 ...
  • 今天整理了一下瀏覽器對JS的相容問題,希望能給你們帶來幫助,我沒想到的地方請留言給我,我再加上; 常遇到的關於瀏覽器的寬高問題: event事件問題: DOM節點相關的問題,我直接封裝了函數,以便隨時可以拿來使用: document.getElementsByClassName問題: 獲取元素的非行 ...
  • 1、v-if 指令可以完全根據表達式的值在Dom中生成或移除一個元素。 例如: <div id ="example"> <p v-if="greeting">Hello!</p> </div> <script> new Vue({ el:"#example", data:{ greeting:fal ...
  • 1.盒子模型 標準CSS盒子模型=content+padding+border+margin; IE盒子模型:content+margin(content裡面已經包含了padding+margin); 2.行內元素?塊級元素?空元素? 行內元素:a、b、span、img、input、strong、s ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...