整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          JavaScript變量

          JavaScript變量

          avaScript 變量松散類型的本質,決定了它只是在特定時間用于保存特定值的一個名字而已。由于不存在定義某個變量必須要保存何種數據類型值的規則,變量的值及其數據類型可以在腳本的生命周期內改變。

          1.基本類型和引用類型的值

          JavaScript變量分為基本數據類型和引用類型。

          基本數據類型包括:Undefined、Null、Boolean、Number和String

          引用數據類型的值是保存在內存中的對象

          2.動態的屬性

          定義一個基本數據類型與引用類型的值是類似的:創建一個變量并為該變量賦值。對于引用類型的值,我們可以動態為其添加屬性和方法,也可以改變和刪除其屬性和方法。

          var person=new Object(); 
          person.name="Nicholas"; 
          alert(person.name); //"Nicholas"
          復制代碼

          對于基本數據類型,不可添加或刪除屬性、方法,盡管這不會導致任何錯誤。

          var name="Nicholas"; 
          name.age=27; 
          alert(name.age); //undefined
          復制代碼

          3.復制變量值

          除了保存方式不同外,在從一個變量向另一個變量復制基本數據類型和引用類型值時,也存在不同。

          如果從一個變量向另一個變量復制基本數據類型的值,會在變量對象上創建一個新值,然后把該值復制到新變量分配的位置上。

          當從一個變量向另一個變量復制引用類型的值時,同樣也會將存儲在變量對象中的值復制一份放到為新變量分配的空間中。不同的是,這個值的副本實際上是一個指針,而這個指針指向存儲在堆中的一個對象。復制操作結束后,兩個變量實際上將引用同一個對象。因此,改變其中一個變量,就會影響另一個變量。

          4.傳遞參數

          ECMAScript中所有函數的參數都是按值傳遞的。也就是說,把函數外部的值復制給函數內部的參數,就和把值從一個變量復制到另一個變量一樣。而引用類型值的傳遞,則如同引用類型變量的復制一樣。

          總之,訪問變量有按值訪問與按引用訪問兩種方式,而參數只能按值傳遞。

          在向參數傳遞基本類型的值時,被傳遞的值會被復制給一個局部變量(即命名參數,就是arguments對象中的一個元素)。在向參數傳遞引用類型的值時,會把這個值在內存中的地址復制給一個局部變量,因此這個局部變量的變化會反映在函數的外部。

          function addTen(num) { 
            num +=10; 
            return num; 
          }
          var count=20; 
          var result=addTen(count); 
          alert(count); //20,沒有變化
          alert(result); //30
          復制代碼

          5.檢測類型

          對于基本數據類型的檢測,可用typeof操作符,可確定一個變量是字符串、數值、布爾值,還是undefined。

          var s="Nicholas"; 
          var b=true; 
          var i=22; 
          var u; 
          var n=null; 
          var o=new Object(); 
          alert(typeof s); //string 
          alert(typeof i); //number 
          alert(typeof b); //boolean 
          alert(typeof u); //undefined 
          alert(typeof n); //object 
          alert(typeof o); //object
          復制代碼

          對于引用類型的檢測,可用instanceof操作符。如果變量是給定引用類型(根據它的原型鏈來識別鏈)的實例,那么instanceof操作符就會返回true。

          alert(person instanceof Object); // 變量 person 是 Object 嗎?
          alert(colors instanceof Array); // 變量 colors 是 Array 嗎?
          alert(pattern instanceof RegExp); // 變量 pattern 是 RegExp 嗎?
          復制代碼

          根據規定,所有引用類型的值都是 Object 的實例。因此,在檢測一個引用類型值和 Object 構造函數時,instanceof 操作符始終會返回 true。當然,如果使用 instanceof 操作符檢測基本類型的值,則該操作符始終會返回false,因為基本類型不是對象。


          在之前的一篇文章《前端開發過程中的HTML規范,來學習一下吧》中,我們有講過前端開發過程中需要注意到的HTML規范問題,今天這篇文章我們繼續來看下關于Javascript的規范問題。

          Javascript

          全局命名與IIFE

          我們總是會在Javascript文件中定義變量,但是一不留神就會將其定義成全局變量,如果引用的外部JS文件較多,很容易出現全局變量污染的情況。

          我們不推薦總是在全局空間定義變量的行為,因為所有在全局空間定義的變量都是掛在window對象上,很容易出現變量污染,如下所示。

          不推薦-全局變量

          IIFE就可以防止出現全局變量污染的情況,IIFE是立即執行的函數表達式,在IIFE內部會創建一個封閉的作用域,內部定義的變量不會影響外部的執行環境,而且可以通過參數傳遞的形式引用外部變量,最重要的一點是在函數執行完后會立即釋放占用的內存。

          我們推薦使用下面這種寫法。

          推薦寫法-IIFE

          IIFE用法

          為了避免全局變量的干擾,我們建議所有腳本文件都從IIFE開始。

          我們都知道之所以叫立即執行的函數表達式,是因為在函數表達式后面會多一個執行的括號。這個執行的括號可以出現在兩個地方,不管是在內部還是外部,都是有效的。但是為了讓整個函數表達式看起來像一個整體,我們推薦將括號寫在里面。

          因此我們不推薦以下寫法。

          不推薦寫法

          而推薦以下寫法。

          推薦寫法

          同樣,我們可以通過參數傳遞的形式引用外部變量。

          引用外部變量

          定義域和變量提升

          我們都知道在ES5中是沒有塊級作用域概念的,只有函數級作用域,而且由于變量提升的存在,在函數內部聲明的變量都會提升至函數頂部,這就會造成一些難以預料的問題。

          首先我們來看看變量提升是什么樣的情況?看看下面一段代碼。

          變量提升

          上面這段代碼返回的結果是undefined,并不是'Hello Shenzhen',這是因為變量v會在函數內部被提升至函數頂部,實際執行的其實是下面這段代碼。

          實際執行

          為了降低變量提升所帶來的編碼風險,我們應該手動聲明定義的變量和方法,并把其放在函數頂部。

          我們不推薦以下寫法。

          不推薦寫法

          我們推薦以下寫法。

          推薦寫法

          比較判斷

          在編寫判斷相等類型的條件語句時,總是使用嚴格相等(===),這樣可以避免Javascript在執行類型轉換時帶來的問題。

          我們看下面一個例子,定義一個函數,傳入一個數字,如果等于5,則將這個數加5返回。如果不使用嚴格等于,在傳入一個字符串'5'后,會返回'55'。

          沒有使用嚴格相等

          因此,我們推薦在使用相等判斷時都采用嚴格相等(===)。

          分號

          強烈建議在所有結束語句后面加上分號,如果不加上分號會引起一些很難發現的問題。我們看看下面一段代碼。

          代碼

          在上面這段代碼執行后,我們發現即使resultOperation()函數返回-1,與-1相等,后面的method方法仍然被調用。

          這是因為在上面定義的數組末尾沒有加上分號,這個數組會與下面一行的-1當做表達式執行,任何非空數組-1都會返回NaN,NaN與resultOperation的返回結果-1不相等,因此后面的method方法會被執行。

          省略分號不寫,不只是會出現上述的問題,還有很多,這里不一一列舉。

          因此,建議在每個結尾的語句后加上分號,養成一個好的習慣。

          閉包

          閉包作為前端面試題中必不可少的知識點是應該要掌握的,而且在前端開發中經常會涉及到,關于閉包的問題,在我寫的一篇文章《前端面試中不可逃避的閉包問題,你真的了解嗎?》中有詳細介紹,大家可以好好看下。

          結束語

          今天這篇文章詳細的介紹了在前端開發過程中涉及到的Javascript規范問題,可能還不夠全面,大家可以自行補充。

          管理服務專家新鈦云服 林泓輝


          與許多同類語言相比,JavaScript 是一種易于學習的編程語言。但是,如果您想理解、調試和編寫更好的代碼,則需要多加注意一些基本概念。


          在本文中,我們將了解兩個這樣的概念:

          • 執行上下文
          • 變量提升


          作為一個初學者的JavaScript,了解這些概念將有助于您了解this關鍵字,作用域和閉包。



          JavaScript 中的執行上下文



          一般來說,一個 JavaScript 源文件會有多行代碼。作為開發人員,我們將代碼組織成變量、函數、數據結構(如對象和數組)等等。


          語法環境決定了我們如何以及在何處編寫代碼。看看下面的代碼:

          function doSomething() {
           var age=7;
           // Some more code
          }


          在上面的代碼中,變量age在語法上位于函數內部doSomething


          請注意,我們的代碼不會按原樣運行。它必須由編譯器翻譯成計算機可理解的字節碼。通常,語法環境在您的代碼中會有多個。然而,并不是所有的環境都會同時執行。


          幫助代碼執行的環境稱為執行上下文。它是當前正在運行的代碼,以及有助于運行它的一切。可以有很多語法環境,但當前運行的代碼只能有一個執行上下文。


          查看下圖以了解語法環境和執行上下文之間的區別:

          語法環境與執行上下文


          那么在執行上下文中到底發生了什么?代碼被逐行解析,生成可執行的字節碼,分配內存并執行。


          讓我們采用我們在上面看到的相同函數。您認為執行以下行時可能會發生什么?

          var age=7;


          這段源代碼在最終執行之前經歷了以下階段:

          • 標記:在此階段,源代碼字符串分解為多個有意義的塊,稱為Tokens. 例如,代碼var age=7;標記為var , age , = , 7和, ; .
          • 解析:下一個階段是解析,在這個階段,一個標記數組變成一個由語言語法理解的嵌套元素樹。這棵樹被稱為AST(抽象語法樹)。
          • 代碼生成:在這個階段,在解析階段創建的 AST 變成可執行的字節碼。該可執行字節碼隨后由 JIT(即時)編譯器進一步優化。


          下面的動畫圖片顯示了源代碼到可執行字節碼的轉換。

          可執行字節碼的源代碼


          所有這些事情都發生在一個執行上下文中。所以執行上下文是代碼的特定部分的執行環境。


          有兩種類型的執行上下文:

          • 全局執行上下文 (GEC)
          • 函數執行上下文 (FEC)


          每個執行上下文都有兩個階段:

          • 創建階段
          • 執行階段


          讓我們詳細看看它們中的每一個,并更好地理解它們。



          JavaScript 中的全局執行上下文 (GEC)



          每當我們執行 JavaScript碼時,它都會創建一個全局執行上下文(也稱為基本執行上下文)。全局執行上下文有兩個階段。


          創建階段


          在創建階段,創建了兩個獨特的東西:

          • 調用的全局對象window(用于客戶端 JavaScript)。
          • 一個名為this的變量。


          如果代碼中聲明了任何變量,則會為該變量分配內存。該變量使用唯一key進行初始化,并賦值為undefined


          如果代碼中有function ,它會被直接放入內存中。我們將在Hoisting后面的部分中詳細了解這部分。



          執行階段


          代碼執行在這個階段開始。在這里進行全局變量的賦值。請注意,這里沒有調用函數,因為它發生在函數執行上下文中。我們將在后面討論這個問題。


          讓我們通過幾個例子來理解這兩個階段。


          示例 1:加載空腳本


          創建一個名為index.js的空 JavaScript 文件及一個包含以下內容的 HTML 文件:

          <!DOCTYPE html>
          <html lang="en">
          <head>
             <meta charset="UTF-8">
             <meta http-equiv="X-UA-Compatible" content="IE=edge">
             <meta name="viewport" content="width=device-width, initial-scale=1.0">
             <title>Document</title>
             <script src='./index.js'></script>
          </head>
          <body>
            I'm loading an empty script
          </body>
          </html>


          我們使用<script>標簽將空腳本文件導入到 HTML 文件中。


          在瀏覽器中加載 HTML 文件并打開 Chrome DevTools(快捷鍵通常為F12)或其他瀏覽器也是可以的。選擇console選項卡,鍵入window并按回車鍵。您可以看到瀏覽器的Window對象。

          windows對象


          現在,輸入this并按回車鍵。您可以看到和Window對象一樣的this對象。

          'this' 的值


          如果您輸入window===this則會得到返回值true



          好的,那么我們學到了什么?

          • 當我們加載 JavaScript 文件時,即使它是空的,也會創建全局執行上下文。
          • 它在創建階段為我們創建了兩個特殊的東西,即window對象和this
          • 在全局執行上下文中,window對象 和this是相等的。
          • 因為腳本文件是空白的,所以沒有什么可以執行的。所以在執行階段什么也不會發生。


          示例 2:使用變量和函數


          現在讓我們看一個在 JavaScript 文件中包含一些代碼的示例。我們將添加一個變量blog,并為其分配一個值。我們還將定義一個名為logBlog的函數。

          var blog='freeCodeCamp';
          
          function logBlog() {
           console.log(this.blog);
          }


          在創建階段:

          • 全局對象window和變量this被創建。
          • 內存被分配給變量blog和函數logBlog
          • 該變量blog由一個特殊值undefined初始化。該函數logBlog直接放置在內存中。


          在執行階段:

          • freeCodeCamp被分配給變量blog
          • 由于我們已經定義了函數但還沒有調用它,因此函數執行不會發生。我們將調用該函數,看看當我們了解函數執行上下文時會發生什么。



          JavaScript 中的函數執行上下文 (FEC)



          當我們調用一個函數時,會創建一個函數執行上下文。讓我們擴展上面使用的相同示例,但這次我們將調用該函數。

          var blog='freeCodeCamp';
          
          function logBlog() {
           console.log(this.blog);
          }
          
          // Let us call the function
          logBlog();


          函數執行上下文經歷相同的階段,即創建和執行。


          函數執行階段可以訪問一個名為arguments的特殊值。它是傳遞給函數的參數。但在我們的示例中,沒有傳遞任何參數。


          請注意,在全局執行上下文中創建的window對象和this變量仍然可以在此上下文中訪問。


          當一個函數調用另一個函數時,會為新的函數調用創建一個新的函數執行上下文。每個函數中相應的變量只能在對應的執行上下文中使用。



          在 JavaScript 中的變量提升



          讓我們轉到另一個基本概念Hoisting。當我第一次聽說Hoisting時,花了一些時間才理解這個意思。


          在英語中,hoisting 的意思是使用繩索和滑輪提升某物。這可能會誤導您認為 JavaScript 引擎會在特定代碼執行階段拉取變量和函數。接下來,讓我們理解Hoisting的意思。



          JavaScript 中的變量提升


          請看下面的例子并猜測輸出:

          console.log(name);
          var name;    // undefined


          然而,為什么是undefined?如果我們在其他編程語言中使用類似的代碼。在這種情況下,我們將在控制臺得到報錯,指出該變量name未聲明,而我們正試圖在此之前訪問它。但是在JavaScript的執行上下文里:

          在創建階段,

          • 內存被分配給變量name,并且
          • 一個特殊的值undefined被分配給變量。


          在執行階段,

          • console.log(name)語句將執行。


          這種為變量分配內存并賦值為undefined在執行上下文的創建階段使用值進行初始化的機制稱為Variable Hoisting(變量提升)。

          特殊值undefined意味著聲明了一個變量但沒有賦值。


          如果我們為變量分配一個這樣的值:

          name='freeCodeCamp';


          執行階段會將這個值賦給變量。



          JavaScript 中的函數提升



          現在讓我們談談Function Hoisting(函數提升)。它與Variable Hoisting的模式相同。


          執行上下文的創建階段將函數聲明放入內存,并在執行階段執行。請看下面的例子:

          // Invoke the function functionA
          functionA();
          
          // Declare the function functionA
          function functionA() {
          console.log('Function A');
          // Invoke the function FunctionB    
          functionB();
          }
          
          // Declare the function FunctionB
          function functionB() {
          console.log('Function B');
          }


          輸出如下:

          Function A
          Function B
          • 執行上下文為函數創建內存并將整個函數聲明functionA放入其中。
          • 函數創建自己的執行上下文。所以類似的事情也發生了functionB
          • 接下來,函數分別在它們的執行上下文中執行。


          在創建階段將整個函數聲明提前放入內存稱為Function Hoisting



          一些基本規則



          既然我們了解了變量提升的概念,那么讓我們了解一些基本規則:

          • 在代碼中使用變量和函數之前,務必先定義它們。這將減少意外的錯誤,為您的調試減少麻煩。
          • 提升僅用于函數聲明,而不用于初始化。這是一個函數初始化的例子,代碼執行會中斷。
          logMe();
          
          var logMe=function() {
           console.log('Logging...');
          }


          代碼執行將中斷,因為在函數初始化時,變量logMe將作為變量而不是函數被提升。因此,對于變量提升,內存分配將在初始化時發生undefined。這就是我們會得到錯誤的原因:


          函數初始化時出錯


          假設我們嘗試在聲明之前訪問一個變量,然后使用letandconst關鍵字來聲明它。在這種情況下,它們將被提升但不會被分配默認值undefined。訪問此類變量將導致ReferenceError. 下面是一個例子:

          console.log(name);
          let name;


          它會拋出錯誤:


          使用 let 和 const 關鍵字聲明的提升變量時出錯


          如果我們使用var代替let和,相同的代碼將毫無問題地運行const。這個錯誤是因為新的JavaScript 語言的保護機制,防止意外提升可能會導致不必要的麻煩。


          感謝您能看到最后,我希望這篇文章能幫助您更好的理解JavaScript中的執行上下文與變量提升的機制。


          原文:https://www.freecodecamp.org/news/javascript-execution-context-and-hoisting/


          主站蜘蛛池模板: 精品国产免费一区二区三区香蕉| 中文字幕不卡一区| 日韩AV无码久久一区二区| 在线观看一区二区三区视频| 国产一区二区三区在线免费观看| 国产日韩AV免费无码一区二区| 精品无码国产一区二区三区51安| 亚洲av日韩综合一区久热| 欧美av色香蕉一区二区蜜桃小说| 亚洲欧美日韩一区二区三区在线| 国产成人无码AV一区二区| 精品久久久久一区二区三区| 夜色阁亚洲一区二区三区| 国产色综合一区二区三区| 中文字幕精品一区影音先锋| 日韩精品一区二区三区中文精品| 日韩av无码一区二区三区| 性色AV一区二区三区无码| 久久久久人妻一区精品果冻| 综合人妻久久一区二区精品| 亚洲无线码在线一区观看| 国产免费一区二区三区| 国产成人一区二区三区高清| AV怡红院一区二区三区| 精品人妻码一区二区三区| 国产一区二区福利| 少妇一晚三次一区二区三区| 一区精品麻豆入口| 无码成人一区二区| 亚洲国产精品无码第一区二区三区 | 在线精品视频一区二区| 国产成人高清亚洲一区久久| 国偷自产av一区二区三区| 日韩一区二区三区视频| 国产一区二区三区无码免费| 国产精品揄拍一区二区久久| 福利电影一区二区| 成人区人妻精品一区二区三区| 国产一区二区三区高清在线观看 | 一区二区三区福利视频免费观看| 精品一区二区三区四区电影|