JS 全局作用域和局部作用域

一、作用域

1、什麼是作用域(Scope)

 通常來說,一段程序代碼中所用到的名字不總是有效和可用的,而限定這個名字的可用性的代碼範圍就是這個名字的作用域。

JS作用域就是代碼名字(變量)作用的範圍

作用域的目的:是爲了提高程序的可靠性,更重要的是減少命名衝突

2、JS的作用域的分類(ES6之前)

JS作用域可以分爲兩大類:全局作用域 、局部作用域(函數作用域)

(一)全局作用域:

  直接編寫在 script 標籤之中的JS代碼,都是全局作用域;

  或者是一個單獨的 JS 文件中的。

  全局作用域在頁面打開時創建,頁面關閉時銷燬;

  在全局作用域中有一個全局對象 window(代表的是一個瀏覽器的窗口,由瀏覽器創建),可以直接使用。

在全局作用域中,

  • 所有創建的變量都會作爲 window 對象的屬性保存。

  • 所有創建的函數都會作爲 window 對象的方法保存。

 

(二)局部作用域(函數作用域):

  在函數內部就是局部作用域,這個代碼的名字只在函數的內部起作用

  調用函數時創建函數作用域,函數執行完畢之後,函數作用域銷燬;

  每調用一次函數就會創建一個新的函數作用域,它們之間是相互獨立的。

實例分析:

在這個例子裏面 un函數裏面的 局部作用域中 有一個 num 變量,script 標籤的全局作用域中也有一個 num變量。

(一個在全局作用域下,另一個在局部作用域下,雖然兩個變量的變量名相沖突,但是並沒有影響。)

所以,在不同的作用域下,變量名相同也不受影響,這樣就很有效的減少了命名衝突。

<script>
    var num = 10;
    function nu(){
        var num = 20;
        console.log(num);
    }
    nu();
    console.log(num);
</script>

 JS現階段(ES6之前)沒有塊級作用域,被塊級作用域就是用大括號({})包含的就是塊級作用域。

 

二、變量的作用域

在JavaScript中,根據作用域的不同,變量可以分爲兩種:全局變量局部變量

(一)全局變量

  1、在全局作用域下聲明的變量叫做 全局變量在函數外部定義的變量

  2、全局變量在全局(代碼的任何位置)下都可以使用;全局作用域中無法訪問到局部作用域中的變量。

  3、全局變量第一種創建方式:在全局作用域下 var聲明的變量是全局變量

  4、全局變量第二種創建方式:如果在函數內部,沒有使用 var關鍵字聲明直接賦值的變量也屬於 全局變量。(不建議使用)

(變量 num 直接寫在 script標籤下,所以 num是全局變量。)

<script>
var num = 10;
    function nu(){
        console.log(num);
    }
nu(); console.log(num);
</script>

 

 (二)局部變量:

  1、在局部作用域下聲明的變量叫做局部變量在函數內部定義的變量

  2、局部變量只能在函數內部使用,在局部作用域中可以訪問到全局變量。

  3、在函數內部 var 聲明的變量就是局部變量

  4、函數的形參實際上就是局部變量

<script>
    function nu(){
        var num1 = 10;
        num2 = 20;
        console.log(num1);
    }
    nu();
    console.log(num2);
</script>

(三)全局變量和局部變量的區別:

全局變量在任何一個地方都可以使用,全局變量只有在瀏覽器關閉的時候纔會銷燬,比較佔用內存資源

局部變量只能在函數內部使用,當其所在代碼塊被執行時,會被初始化;當代碼塊執行完畢就會銷燬,因此更節省節約內存空間;

 

三、變量的聲明提前和函數的聲明提前

(一) 變量的聲明提前

使用 var 關鍵字聲明的變量,會在所有的代碼執行之前被聲明。(但是不會賦值)

全局變量即使是寫在最下面,也相當於在所有代碼之前的最上面聲明的變量。

  等價於 

 (在這個例子中最終結果返回的是 undefined,這是因爲 變量a 就相當於在所有代碼最上面被聲明,但下面才被賦值,所以結果是 undefined未定義)

如果聲明變量的時候不使用 var 關鍵字,那麼變量就不會被聲明提前。

 

(如果不寫 var 關鍵字,變量聲明就無法提前,所以在 console.log前面就找不到 變量,所以返回結果報錯)

(二) 函數的聲明提前

使用函數聲明形式創建的函數function 函數名() {};

它會在所有代碼執行之前就被創建。所以可以在函數聲明之前被調用

 等價於

使用函數表達式創建的函數var 變量名 = function(){};

不會被聲明提前,所以不能再聲明前調用。

 

 

四、作用域鏈

只要是代碼,就有一個作用域,寫在函數內部的就叫做局部作用域;

如果函數中還有函數,那麼在這個作用域中又可以誕生一個作用域;

當在函數作用域中操作一個變量的時候,會先在自身作用域中查找,如果有就直接使用,如果沒有就向上級作用域中尋找。如果全局作用域中也沒有,那麼就報錯。

根據內部函數可以訪問可以訪問外部函數變量的這種機制,用鏈式查找決定哪些數據能被內部函數訪問,就稱爲函數作用域鏈

作用域鏈:內部函數訪問外部函數的變量,採取的是鏈式查找的方法來決定取那個結構,這種結構稱之爲作用域鏈

作用域鏈的原則:就近原則

(作用域鏈採用鏈式查找的方式,一層一層向上查找,先查找外面的嵌套的函數是否有所需內容,找到就輸出相應的結果,如果沒有再向上查找。就近原則

實例一:


 下面代碼最終輸出的結果是多少?

 思路分析:

 按照鏈式查找先到上一級查找,輸出內容在2級鏈,向上到 1級鏈去查找,如果 1級鏈也沒有就繼續向上查找。如果都找不到就會返回 undefined(未定義)。

因爲1級鏈中有 unm 值,所以輸出num結果就是 123。

 

實例二:


 下面代碼最終輸出的結果?

注意:在更長的結構中畫圖分析太過於麻煩,可以從輸出目標console.log(); 位置向外層的結構看,尋找最近的變量

var a = 1;
    function fn1(){
        var a=2;
        var b='22';
        fn2();
        function fn2(){
            var a =3;
            fn3();
            function fn3(){
                var a=4;
                console.log('a= ' + a);  //求 a的值
                console.log('b= ' + b);  //求 b的值
            }
        }
    }
    fn1();

最終結果是:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章