JavaScript之函數作用域


  有過類似C語言編程經驗的同學應該都知道“塊級作用域(block scope)”:花括號內的每一段代碼都具有各自的作用域,而且在聲明它們的代碼段之外是不可見的。而在JavaScript中是沒有塊級作用域的,JavaScript取而代之地使用了“函數作用域(function scope)”:變量在聲明它們的函數體以及這個函數體嵌套的任意函數體內都是有定義的。

  光用文字解釋平白無味,先來段讓你為之一振的代碼:

function myTest(num){
    var i = 0;
    if(num == 222){
        var j = 0;
        for(var k=0; k<2; k++){
            console.log(k);
        }
        console.log(k);
    }
    console.log(i);
    console.log(j);
    console.log(k);
    console.log(m);
}
myTest(111);

  請認真閱讀代碼,仔細思考,認真回答問題。下面給出瀏覽器中運行的結果,檢驗下自己的答案吧!

  如果你完全回答正確,並且已經明白所有原因,那么就沒必要看下去了;如果你還未明白原因並且有一顆好奇、意欲一探究竟的心,那么就下來就要認真看完本博客了。

  既然不懂,那就再繼續認真看解釋唄:JavaScript的函數作用域是指在函數內聲明的所有變量在函數體內始終是可見的,這就意味着變量在聲明之前甚至已經可用了。JavaScript這個特性被非正式地稱為聲明提前,即JavaScript函數里聲明的所有變量(但不涉及賦值)都被提前至函數體的頂部了。這么說上面的例子中代碼就等同於

function myTest(num){
    var i,j,k;
    i = 0;
    if(num === 222){
        j = 0;
        for(k=0; k<2; k++){
            console.log(k);
        }
        console.log(k);
    }
    console.log(i);
    console.log(j);
    console.log(k);
    console.log(m);
}

  所以即使代碼運行時沒有進入if條件語句中,但變量i,j,k已經聲明了(即使是在for循環中聲明的k聲明也提前了呢),只是i初始化為0,而j,k沒有初始化而已,所以j,k輸出是undefined。至於m,因為它根本就沒有聲明過,所以調用時就報錯了。現在明白了吧,下面再來一題自己檢測下掌握情況吧:

var str1= "out1";
var str2 = "out2";
function myTest(){
    console.log(str1);
    console.log(str2);
    var str2 = "inner";
    console.log(str2);
}
myTest();

  結果是什么呢?下面給出瀏覽器的回答,檢驗一下你心中的答案吧!

  給JS學習者一個建議:盡量將變量聲明放在函數體頂部,而不是將聲明放在使用變量之處,這種做法會使代碼清晰地反映了真實的變量的作用域。

補充內容:經過最近的學習,發現這篇博客寫的時候自己學的還是不夠全面,下面增加一段補充內容:

  ①var 和 function 都是聲明語句,它們聲明或定義變量或函數。

  ②變量在聲明它們的腳本或函數中都是有定義的,變量聲明語句會被“提前”至腳本或者函數的頂部,但是初始化操作還在原來賦值語句的位置執行,未初始化的變量的值是undefined。

  ③函數聲明語句(注意不是函數定義表達式)中的函數會被顯示地“提前”到了腳本或函數的頂部,因此它們在整個腳本和函數內都是可見的,這個時候函數名稱和函數體均會提前,也就是說可以在聲明一個JavaScript函數之前調用它。關於函數定義表達式,即使用var來定義一個函數,這種形式只有變量聲明提前了,變量的初始化仍在原來位置,這時候就不能在初始化之前調用這個函數了。如果不懂這兩種函數定義的形式,請繼續閱讀:下面馬上就有例子來區別說明了。

  這里要重點補充的就是:JavaScript中不止在函數中有變量聲明提前,在函數外也有變量聲明提前。下面還是用代碼說話:

 

  正如預料之中,console.log(aa)輸出結果是undefined,而console.log(bb)卻因為變量bb在任何位置都沒有聲明所以就報錯。

  再來說說函數聲明語句中函數提前的問題,先看一個函數聲明語句的例子:

 

  看到了吧,myConsoleLog 函數就是通過函數聲明語句定義的,所以它的函數名和函數體都會提前至頂部,所以在聲明語句之前就可以調用它了,下面繼續說一個函數定義表達式的例子:

 

  這次的 myConsoleLog 函數就是通過函數定義表達式定義的,所以myConsoleLog 就是使用var 聲明的其他普通變量一樣,只有聲明提前了,但它初始化還是在第三行那里,所以第一行輸出變量myConsoleLog的值就是undefined嘍,第二行調用更是會報錯了,因為現在myConsoleLog還只是一個值為undefined的普通變量呢,怎么可以作為一個函數來調用啊。

  補充就先到這里吧!!!

下面是在下衷心送給各位看官的五個歡迎(O(∩_∩)O~):歡迎轉載、歡迎收藏、歡迎評論、歡迎點贊、歡迎推薦!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM