淺談javascript中的作用域

来源:http://www.cnblogs.com/chenmeng0818/archive/2016/07/19/5683315.html
-Advertisement-
Play Games

所謂的作用域,可以簡單理解為一個可以讀、寫的範圍(區域),有些js經驗的同學可能會說:"js沒有塊級作用域",js除了全局作用域外,只有函數可以創建作用域。作用域的一個好處就是可以隔離變數。 我們通過一些例子來幫助我們理解js中的作用域。 如果對作用域一點不瞭解的同學可能會說 alert的是1或者報 ...


所謂的作用域,可以簡單理解為一個可以讀、寫的範圍(區域),有些js經驗的同學可能會說:"js沒有塊級作用域",js除了全局作用域外,只有函數可以創建作用域。作用域的一個好處就是可以隔離變數。

我們通過一些例子來幫助我們理解js中的作用域。

1 alert(a);
2 var a = 1;

如果對作用域一點不瞭解的同學可能會說 alert的是1或者報錯;但實際上是undefined;

說到這裡,我們首先說一下js逐行解析代碼之前做的一些準備工作,

js在逐行讀代碼之前,會做一些“預解析”工作,會先提前找到一些”小東西”,當然”js解析器“不會隨便找一些數據的,它會根據var,function,參數來找。

”js解析器“它比較”懶“,在正式運行代碼之前都會給var聲明的變數賦值為undefined,也就是var a = undefined;會把整個函數看作一個代碼塊,不去管裡邊有多少代碼。參數等到後邊例子中會說。

當所有準備工作都做好後,“JS解析器”就開始逐行執行代碼了,現在我們來分析開始的這個例子就很容易明白為什麼是undefined了。

再來看下邊這個例子

1 alert(a);
2 var a = 1;
3 alert(a);
4 var a = 2;
5 alert(a);

我們來一點點分析這個

首先 ”預解析“: 解析器會找var

讀到第二行時  a = undefined;

讀到第四行時 依然  a = undefined;

正式逐行執行代碼:

第一行 alert:undefined 

第二行  a = 1;

第三行 alert:1;

第五行 alert:2

接著看下邊這個例子

1 alert(a);                    
2 var a = 1;
3 alert(a);                    
4 function a (){ alert(2); }
5 alert(a);                    
6 var a = 3;        
7 alert(a);                    
8 function a (){ alert(4); }
9 alert(a);    

我們依然來一點點分析這個

首先 ”預解析“: 解析器會找var function;

讀到第二行時 a = undefined;

讀到第四行時 a = function a (){ alert(2);} //所有的函數,在正式運行代碼之前,都是整個函數塊;變數遇到重名的,只留一個變數,如果變數和函數重名,就只留下函數。

讀到第六行時,a = function a (){ alert(2);}

讀到第八行時,a = function a (){ alert(4);}

正式逐行執行代碼:

第一行 alert: function a (){ alert(4);} 

第二行  a = 1; //表達式可以修改預解析的值!

第三行 alert:1;

第四行 函數沒有調用,略過;

第五行 alert:1;

第六行 a = 3;

第七行 alert:3

第八行 函數沒有調用,略過;

第九行  alert:3

如圖所示:

 

繼續看例子:

1 var a = 1;
2 function fn1(){
3     alert(a);      //undefined                   
4     var a = 2;
5 }
6 fn1();
7 alert(a);   //1

首先 ”預解析“: 解析器會找var function

讀到第一行時 a = undefined;

讀到第二行時 fn1 = function fn1 (){alert(2);var a = 2;}                             

正式逐行執行代碼: 第一行 a = 1;

第六行 函數調用,進入函數作用域 在函數作用域內依舊是先預解析,再逐行執行

  函數內預解析:a = undefined;

  執行:alert:undefined;

  a = 2;  //此時的a僅為函數作用域中的a,不會影響全局中的a

函數執行完畢,回到全局作用域;

第七行 alert:1;

繼續:

1 var a = 1;
2 function fn1(){
3     alert(a);       //1             
4     a = 2;
5 }
6 fn1();
7 alert(a);    //2

這個例子上邊那個例子唯一的區別就是函數中的a沒有var,只分析其中關鍵的地方

在函數作用域中 第三行alert(a),由於函數中沒有var a,所以"解析器"會到函數的作用域的上一級作用域去尋找a(作用域上下級關係的確定就看函數是在哪個作用域下創建的,在哪個作用域下創建,就是哪個作用域的下級),此時函數的上一級是全局作用域,在全局作用域中,a = 1,所以此時第三行 alert:1,接著第四行,a = 2賦值,依然是函數作用域中沒有a, 所以在上一級作用域,也就是全局作用域中找到a,修改全局作用域中的a, 所以會使全局作用域中的a = 2, 因此第七行 alert:2;

這點要理解清楚,註意有無var的區別。

接著來:

1 var a = 1;
2 function fn1(a){
3     alert(a);     //undefined                
4     a = 2;
5 }
6 fn1();
7 alert(a);   // 1

這個例子和上一個的區別就是多了個參數,參數的作用相當於局部變數,也就是在函數中預解析會有var a = undefined,所以第三行 alert:undefined,第四行 a = 2 改的是函數作用域中的a,不影響全局中的a,第七行alert:1;

接著:

1 var a = 1;
2 function fn1(a){
3     alert(a);                        // 1
4     a = 2;
5 }
6 fn1(a);
7 alert(a);          // 1

這個例子又與上一個有些區別,在第六行函數調用時傳了個實參進去,第六行函數實參的a是全局變數a = 1的1,函數執行時,第二行 a = 1,所以第三行alert:1,第七行alert:1。

註意這幾個例子之間的區別,別混淆了。

再來一個:

1 var a = 1;
2 function en(){
3     var a = 2;
4     fn();
5 }
6 function fn(){
7     alert(a);           //1
8 }
9 en();

fn中的a未聲明,要到創建這個函數的那個作用域中取值——是“創建”,而不是“調用”這個函數的作用域中。

 

如有錯誤,歡迎指正。


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

-Advertisement-
Play Games
更多相關文章
  • css是網頁的外衣,好不好看全憑css樣式,而佈局是css中比較重要的部分,下麵來分析一下常見的幾種佈局。 流動模型 流動模型是網頁佈局的預設模式,也是最常見的佈局模式,他有兩個特點: 1.塊狀元素都在所處包含元素內自上而下按順序垂直延伸分佈。常見的塊狀元素有:div,p,ul,ol,h1~h6,a ...
  • 方法:直接判斷瀏覽器是否支持某個CSS屬性才是王道,document.documentElement.style 如:判斷是否支持 transform if( 'MozTransform' in document.documentElement.style || 'WebkitTransform' ...
  • 作者:白狼 出處:http://www.manks.top/javascript-dynamic-event.html 本文版權歸作者,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。 其所謂的動態添加事件實質就是指js中的事件委托。 我們知道 ...
  • 線上實例 實例演示 使用方法 複製 複製 下載 ...
  • 本文標題的這副圖片,是用Phosotshop製作的。但是,在搜索引擎中你卻無法搜索到它,搜索引擎還沒有強大到能夠識別圖片裡面的文字。並且由於圖片的體積不算太小,可能網速慢的網友在瀏覽的時候不得不耐心的等待圖片的刷新。那麼,有沒有一種新的方法可以避免這些缺點呢? 有的,HTML5和CSS3就可以滿足你 ...
  • 別人的代碼,拿過來調,發現修改功能都不能用,修改時通過ajax發json獲取數據的,看chrome開發者工具發現有發送數據,也有返回值; 發起請求並獲取數據,發現回調函數不執行! php返回數據代碼: 返回的數據在瀏覽器里看上去也很正常: {"data":{"id":"1","name":"admi ...
  • 我們先來看一道題目 1 2 3 4 var write = document.write; write("hello"); //1.以上代碼有什麼問題 //2.正確操作是怎樣的 1 2 3 4 var write = document.write; write("hello"); //1.以上代碼有 ...
  • 摘要: 之前項目用過Less,現在負責的項目也要使用,所以就總結下Less,也方便以後查看。本文主要是講瀏覽器端如何使用Less。 簡介: LESS是一種由Alexis Sellier設計的動態層疊樣式表語言。LESS 是開源的,其第一個版本由Ruby寫成,但在後續的版本當中,Ruby逐漸被替換為J ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...