JS作用域與JS代碼的運行

第7章 作用域與JS代碼的運行

作用域:變量可以起作用的範圍和區域

7.1 全局變量和局部變量

7.1.1 局部變量與全局作用域

在任何地方都可以訪問到的變量就是全局變量,全局變量所在的區域就是全局作用域

7.1.2 局部變量

只在固定的代碼片段內可訪問到的變量,最常見的例如函數內部的變量,就是局部變量。局部變量所在的區域就是局部作用域(函數作用域)

不使用var聲明的變量是全局變量,不推薦使用。
變量退出作用域之後會銷燬,全局變量關閉網頁或瀏覽器纔會銷燬


: 除了全局作用域和局部作用域外 , 還有塊級作用域, 如 for循環 和 if語句 中定義的變量

7.1.3 塊級作用域 :

一對大括號就可以看成是一塊 , 在這對大括號裏使用的變量 , 只能在這個區域中使用 . 但是在js中 , 在這個塊級作用域中定義的變量 , 外面也能使用 就是沒有塊級作用域 , 只有函數除外


for(var i=0; i<5; i++){
    var a = 9;  //塊級作用域
    console.log(a+i);
}

7.1.4 隱式全局變量 :

聲明的變量沒有var , 就叫做全局變量


a = 2;  //隱式全局變量
function f1(){
    b = 3;   //隱式全局變量
}

7.2 JS代碼的運行


console.log(s); //undefined
var s = 2;

JavaScript代碼的執行是由瀏覽器中的JavaScript解析器來執行的。

JavaScript解析器執行JavaScript代碼的時候,分爲兩個過程:預解析(編譯)過程和代碼執行過程

預解析過程:

  1. 語法檢查,如果有錯誤,直接停止後續步驟不再運行。

  2. 把變量和函數的聲明提升到當前作用域的最前面,只會提升聲明,不會提升賦值和調用。

  3. 先提升變量後提升函數,如果函數和變量同名,則被替換;

代碼執行過程

變量的賦值,函數的調用,循環判斷等,根據代碼由上往下順序執行;


var a = 25;
function abc (){
  alert(a);//undefined
  var a = 10;
}
abc();

// 如果變量和函數同名的話,函數優先做提升
console.log(a);
function a() {
  console.log('aaaaa');
}
var a = 1;
console.log(a);

// 1、----------------
var num = 10;
fun();
function fun() {
    console.log(num); //undefined
    var num = 20;
}

// 2、----------------
var a = 18;
f1();
function f1() {
    var b = 9;
    console.log(a); //undefined
    console.log(b); // 9
    var a = '123'; 
}

7.3 詞法作用域

變量的作用域是在定義時決定而不是執行時決定的,也就是說詞法作用域取決於編譯階段,通過靜態分析就能確定,因此詞法作用域也叫做靜態作用域。

在 js 中詞法作用域規則:

  • 函數允許訪問函數外的數據.

  • 整個代碼結構中只有函數可以限定作用域.

  • 作用域規則首先使用提升規則分析

  • 如果當前作用規則中有名字了, 就不考慮外面的名字


var num = 123;
function foo() {
  console.log( num );
}
foo();

if ( false ) {
    var num = 123;
}
console.log( num ); // undefiend

也就是說:

函數內部可以訪問函數外部的變量,但是函數外部不可以訪問函數內部的變量;

函數內部如果有變量,則優先使用內部的變量,如果函數內部沒有,纔會使用函數外部的變量;

7.4 變量的提升


console.log(a); // undefined
var a = 2;

console.log(a); //   a is not defined
  • 變量提升

    定義變量的時候,變量的聲明會被提升到作用域的最上面,變量的賦值不會提升。

  • 函數提升

    JavaScript解析器首先會把當前作用域的函數聲明提前到整個作用域的最前面


f();
function f(){
    console.log(12); //12
}

var f = 1;
function f(){
    console.log(12); //12
}
// 由於函數提升在前,所以被變量聲明替換了;
// 執行階段,變量賦值爲1,不再是一個函數,
f(); // f is not a function

注:不管是普通變量還是函數,儘量不要出現重名;

7.5 作用域鏈

只有函數可以製造作用域結構, 那麼只要是代碼,就至少有一個作用域, 即全局作用域。凡是代碼中有函數,那麼這個函數就構成另一個作用域。如果函數中還有函數,那麼在這個作用域中就又可以誕生一個作用域。
將這樣的所有的作用域列出來,可以有一個結構: 函數內指向函數外的鏈式結構。就稱作作用域鏈。



var a = 1;
function fn1(){
    function fn2(){
        function fn3(){
            console.log(a);
        }
        fn3();
    }
    fn2();
}
fn1();

var a = 1;
function fn1(){
    var a = 2;
    function fn2(){
        var a = 3;
        function fn3(){
            console.log(a);
        }
        fn3();
    }
    fn2();
}
fn1();

7.6 預解析

JavaScript代碼是由瀏覽器中的JavaScript解析器來執行的, JavaScript解析器執行JavaScript代碼的時候,分爲兩個過程 , 預解析過程和代碼執行過程.

  • 把變量的聲明提升到當前作用域的最前面 , 只會提升聲明 , 不會提升賦值

  • 函數的聲明提升到當前作用域的最前面 , 只會提升聲明 , 不會提升調用

  • 先提升var , 再執行function


//1--------------------
var num;
console.log(num);   //undefined

//2---------------------
function f1(){
    console.log(111);
}
f1();  //2222
function f1(){
    console.log(2222);
}
f1();  //2222

//3----------------------
function f1(){
   console.log(num);   //undefined
   var num = 10;
}
f1();
console.log(num);   //報錯

預解析中 , 變量的提升 , 只會在當前的作用域中提升 , 提前到當前的作用域的最上面

函數中的變量只會提前到函數的作用域的最前面 , 不會出去

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