關於JS變數提升的一些坑

来源:http://www.cnblogs.com/luqin/archive/2016/01/27/5164132.html
-Advertisement-
Play Games

JS 變數提升 函數提升


1 function log(str) {
2    // 本篇文章所有的列印都將調用此方法
3    console.log(str);
4 }

 

函數聲明和變數聲明總是會被解釋器悄悄地被“提升”到方法體的最頂部

變數聲明、命名、提升


在JS中, 變數有4種基本方式進入作用域:

  • 語言內置: 所有的作用域里都有this和arguments;(需要註意的是arguments在全局作用域是不可見的)
  • 形式參數: 函數的形式參數會作為函數體作用域的一部分;
  • 函數聲明: 像這種形式: function foo() {};
  • 變數聲明: 像這樣: var foo;

 

變數提升

 

function test1() {
      a = 5;
      log(a); 
      log(window.a); 
      var a = 10;
      log(a); 
    }
    test1();

依次會輸出 5 、undefined 、10   因為在解析時候是等價於

1 var a;
2 a=5;
3 log(a);
4 log(window.a);
5 a=10;
6 log(a);

 接著看另外一個例子:

 1 function test2() {
 2    var a = 1;
 3    var b = 2;
 4    var c = 3;
 5 }
 6 /*test2中的語句,是這樣被執行的  這個時候就把變數提升了
 7     
8 function test2(){
9 var a,b,c; 10 var a = 1; 11 var b = 2; 12 var c = 3; 13 } 14 */

只有函數級作用域,if語句不會有:test3():

function test3(){
var a = 1;
log(a); // 1 
if (true) {
var a = 2;
log(a); //2 
}
log(a); // 2 
}

 

函數的提升


我們寫JS的時候,通常會有兩種寫法:

  • 函數表達式 var fn=function fn(){}
  • 函數聲明方式 function fn(){}

我們需要重點註意的是,只有函數聲明形式才能被提升。
變數賦值並沒有被提升,只是聲明被提升了。但是,函數的聲明有點不一樣,函數體也會一同被提升

 1 function test3() {
 2    fn();
 3    function fn() {
 4      log("我來自fn");
 5    }
 6  }
 7  test3();
 8  function test4() {
 9    fn(); // fn is not a function
10    var fn = function fn() {
11      alert("我來自 fn  test4");
12    }
13  }
14  test4();

 

 

函數表達式需要註意的

  • 在function內部,fn完全等於fn1
  • 在function外面,fn1則是 not defined
 1 function test5() {
 2       var fn = function fn1() {
 3         log(fn === fn1); // true
 4         log(fn == fn1); // true
 5       }
 6       fn();
 7       log(fn === fn1); // fn1 is not defined
 8       log(fn == fn1);  // fn1 is not defined
 9     }
10     test5();

!相容
// b();
// var a = function b() {alert('this is b')};
// 則ie下是可以執行b的. 說明不同瀏覽器在處理函數表達式細節上是有差別的.

 

補充一點函數表達式

定義裡面的指定的函數名是不是被提升的

 1 function text7() {
 2   a(); // TypeError "a is not a function" 
 3   b();
 4   c(); // TypeError "c is not a function" 
 5   d(); // ReferenceError "d is not defined"
 6 
 7   var a = function() {};    // a指向匿名函數 
 8   function b() {};          // 函數聲明 
 9   var c = function d() {};  // 命名函數,只有c被提升,d不會被提升。
10 
11   a();
13   b();
14   c();
15   d(); // ReferenceError "d is not defined"
16 }
17 text7();

 

大家先看下麵一段代碼test6,思考一下會列印什麼?

 1 function text6() {
 2    var a = 1;
 3    function b() {
 4      a = 10;
 5       return;
 6       function a() {}
 7     }
 8     b();
 9     log(a);         //
10 }
11 text6();

||

||

||

|| 輸出在下麵

||

||

||

||

||

||

what? 什麼鬼?為什麼是1?
這裡需要註意的是,在function b()中,
var = a // function 類型的
a=10; // 重新把10複製給a,  此時的a是function b()中的內部變數
return;
function a() {} // 不會被執行

所以,外面輸出的a 依舊是最開始定義的全局變數

 

函數的聲明比變數的聲明的優先順序要高

 1 function text6() {
 2   function a() {}
 3   var a;
 4   log(a);                //列印出a的函數體
 5 
 6   var b;
 7   function b() {}
 8   log(b);                 //列印出b的函數體 
 9 
10   // !註意看,一旦變數被賦值後,將會輸出變數
11   var c = 12
12   function c() {}
13   log(c);                 //12
14   
15   function d() {}
16   var d = 12
17   log(d);                //12
18 }
19 text6();

 

變數解析的順序


一般情況下,會按照最開始說的四種方式依次解析

  • 語言內置:
  • 形式參數: 
  • 函數聲明: 
  • 變數聲明: 

 也有例外:

  • 內置的名稱arguments表現得很奇怪,看起來應該是聲明在形參之後,但是卻在聲明之前。這是說,如果形參裡面有arguments,它會比內置的那個優先順序高。所以儘可能不要在形參裡面使用arguments;
  • 在任何地方定義this變數都會出語法錯誤
  • 如果多個形式參數擁有相同的名稱,最後的那個優先順序高,即便是實際運行的時候它的值是undefined;

 

CAO!這麼多坑,以後腫麽寫代碼?


用var定義變數。對於一個名稱,在一個作用域裡面永遠只有一次var聲明。這樣就不會遇到作用域和變數提升問題。

ECMAScript參考文檔關於作用域和變數提升的部分:
    如果變數在函數體類聲明,則它是函數作用域。否則,它是全局作用域(作為global的屬性)。變數將會在執行進入作用域的時候被創建。塊(比如if(){})不會定義新的作用域,只有函數聲明和全局性質的代碼(單個JS文件)才會創造新的作用域。變數在創建的時候會被初始化為undefined。如果變數聲明語句裡面帶有賦值操作,則賦值操作只有被執行到的時候才會發生,而不是創建的時候。

 

最後,

由於時間倉促,demo有很多不足之處,多諒解

 


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

-Advertisement-
Play Games
更多相關文章
  • lambda只是一個表達式,函數體比def簡單很多。 lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。 lambda表達式是起到一個函數速寫的作用。允許在代碼內嵌入一個函數的定義。 如下例子: 定義了一個lambda表達式,求三個數的和。 再看一個例
  • 複習之餘,做點筆記<C語言之數據類型> 一、整數數據類型 (1)整數數據類型 整數類型 char 字元型變數 1位元組(8Bit) short 短整型 2位元組(16Bit) int 整形變數 取決於編譯器 long 長整形變數 取決於編譯器 對int的理解:在電腦中有CPU和記憶體,在CPU中有寄存器
  • 實現情況: 可自主註冊, 登陸系統可購物,充值(暫未實現),查詢餘額。 擼了兩天一夜的代碼,不多說,直接上碼,註釋神馬的後面再說 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 shopping_list = [ 4 ['Iphone 6s pl...
  • http://www.tuicool.com/articles/MzayMri 電腦剛出世時,編程卡片為八十列;無獨有偶,Unix 早期的終端,每行最大顯示字元數也為八十個;Unix 大多文本工具也沿襲了這傳統,至今在 Maillist 上可以看到不少資深 Linux 工程師習慣把郵件正文 wra...
  • 在之前的Java技術彙總文章里,向大家介紹了Java入門學習的基礎資料,今天小編彙總了5篇Java技術進階實操的乾貨,趕緊來看看吧!另外,喜歡寫博客的工程師博主可以加工程師博主交流群:391519124,分享你的博文,和大牛們一起交流技術~一、Stack Overflow 上人氣爆表的10個 Jav...
  • 寫爬蟲之前,首先要明確爬取的數據。然後,思考從哪些地方可以獲取這些數據。下麵以一個實際案例來說明,怎麼尋找一個好的爬蟲策略。(代碼僅供學習交流,切勿用作商業或其他有害行為) 1).方式一:直接爬取網站 目標網址:http://chanyouji.com/ 註意:這個網站會攔截IP,訪問次數過多...
  • 背景 今天的分享主要來自我之前的工作經驗以及平時的學習總結和思考。我之前的背景主要是做框架、系統和平臺架構,之前的工作過的公司eBay、攜程、唯品會都是平臺型互聯網公司,所以今天主要帶著平臺架構視角和大家分享心得體會。架構的視角每個人都不一樣,可以說一萬種眼光,有業務架構、安全架構、平臺架構、數據架
  • NodeJS新手攻略 1、在官網(https://nodejs.org/)下載node安裝包進行安裝windows和Linux 版本都有 這裡說下windows系統安裝 安裝nodeJS直接下一步 安裝過程中可選擇安裝路徑 node -v npm -v 直接cmd使用nodejs命令查看是否安裝成功
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...