首頁  >  文章  >  web前端  >  JavaScript作用域的全面解析(附程式碼)

JavaScript作用域的全面解析(附程式碼)

不言
不言轉載
2019-04-03 10:27:592132瀏覽

這篇文章帶給大家的內容是關於JavaScript作用域的全面解析(附程式碼),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。

作用域決定了變數的生命週期和可見性,變數在作用域範圍之外是不可見的。

JavaScript 的作用域包括:模組作用域,函數作用域,區塊作用域,詞法作用域和全域作用域。

全域作用域

在任何函數、區塊或模組範圍之外定義的變數具有全域作用域。可以在程式的任意位置存取全域變數。

當啟用模組系統時,建立全域變數會變得困難,但仍然可以做到這一點。可以在 HTML 中定義一個變量,這個變數需要在函數之外聲明,這樣就可以建立一個全域變數:

<script>
  let GLOBAL_DATA = { value : 1};
</script>
console.log(GLOBAL_DATA);

當沒有模組系統時,建立全域變數會容易很多。在任何文件中的函數外聲明的變數都是全域變數。

全域變數貫穿程式的整個生命週期。

另一種建立全域變數的方法是在程式的任意位置使用window 全域物件:

window.GLOBAL_DATA = { value: 1 };

這樣GLOBAL_DATA 變數會隨處可見。

console.log(GLOBAL_DATA)

不過你也知道這種做法是不好的。

模組作用域

如果不啟用模組,在所有函數之外宣告的變數是全域變數。在模組中,在函數外部宣告的變數都是隱藏的,除非明確導出,否則不可用於其他模組。

匯出使函數或物件可用於其他模組。在這個範例中,我從模組檔案 sequence.js 中導出了一個函數:

// in sequence.js
export { sequence, toList, take };

目前模組可以透過匯入來使用其他模組的函數或物件成。

import { sequence, toList, toList } from "./sequence";

在某種程度上,我們可以認為模組是一個自動執行的函數,它將 import 的資料作為輸入,然後傳回 export 的資料。

函數作用域

函數作用域意味著在函數中定義的參數和變數在函數內的任何位置都可見,但是在函數外部不可見。

下面是一個自動執行的函數,稱為IIFE。

(function autoexecute() {
    let x = 1;
})();
console.log(x);
//Uncaught ReferenceError: x is not defined

IIFE 的意思是立即呼叫函數表達式,是一個在定義後立即運行的函數。

var 宣告的變數只有函數作用域。更重要的是,用 var 宣告的變數被提升到其作用域的頂端。透過這種方式,可以在聲明之前存取它們。看看下面的程式碼:

function doSomething(){
  console.log(x);
  var x = 1;
}
doSomething(); //undefined

這種事不會發生在 let 中。用 let 宣告的變數只能在定義後存取。

function doSomething(){
  console.log(x);
  let x = 1;
}
doSomething();
//Uncaught ReferenceError: x is not defined

var 宣告的變數可以在同一作用域下多次重新宣告:

function doSomething(){
  var x = 1
  var x = 2;
  console.log(x);
}
doSomething();

letconst 宣告的變數不能在同一作用域內重新宣告:

function doSomething(){
  let x = 1
  let x = 2;
}
//Uncaught SyntaxError: Identifier 'x' has already been declared

也許我們可以不必關心這一點,因為var 已經開始變得過時了。

區塊作用域

區塊作用域用花括號定義。它由 {} 分隔。

letconst 宣告的變數可以受到區塊作用域的約束,只能在定義它們的區塊中存取。

思考下面這段關於let 區塊範圍的程式碼:

let x = 1;
{ 
  let x = 2;
}
console.log(x); //1

#相反,var 宣告不受區塊作用域的約束:

var x = 1;
{ 
  var x = 2;
}
console.log(x); //2

另一個常見問題是在循環中使用類似setTimeout() 的非同步操作。下面的循環程式碼將顯示五次數字 5。

(function run(){
    for(var i=0; i<5; i++){
        setTimeout(function logValue(){
            console.log(i);         //5
        }, 100);
    }
})();

帶有 let 宣告的 for 迴圈語句在每次迴圈都會建立一個新的變數並設定到區塊作用域。下一段循環程式碼將會顯示 0 1 2 3 4 5

(function run(){
  for(let i=0; i<5; i++){
    setTimeout(function log(){
      console.log(i); //0 1 2 3 4
    }, 100);
  }
})();

詞法作用域

詞法作用域是內部函數存取定義它的外部作用域的能力。

看這段程式碼:

(function autorun(){
    let x = 1;
    function log(){
      console.log(x);
    };
    
    function run(fn){
      let x = 100;
      fn();
    }
    
    run(log);//1
})();

log 函數是一個閉包。它從父函數 autorun() 引用 x 變量,而不是 run() 函數中的 x 變數。

閉包函數可以存取建立它的作用域,而不是它自己的作用域。

autorun() 的局部函數作用域是 log() 函數的詞法作用域。

作用域鏈

每個作用域都有一個指向父作用域的連結。當使用變數時,JavaScript 會向下查看作用域鏈,直到它找到所要求的變數或到達全域作用域(即作用域鏈的末端)。
看下面這個範例:

let x0 = 0;
(function autorun1(){
 let x1 = 1;
  
 (function autorun2(){
   let x2 = 2;
  
   (function autorun3(){
     let x3 = 3;
      
     console.log(x0 + " " + x1 + " " + x2 + " " + x3);//0 1 2 3
    })();
  })();
})();

內部函數 autorun3()  可以存取本地 x3 變數。也可以從外部函數存取變數 x1x2 和全域變數 x0

如果找不到變量,它將在嚴格模式下傳回錯誤。

"use strict";
x = 1;
console.log(x)
//Uncaught ReferenceError: x is not defined

非嚴格模式也被稱為“草率模式”,它會草率的創建一個全域變數。

x = 1;
console.log(x); //1

總結

在全域作用域中定義的變數可在程式的任何位置使用。

在模組中,在函數外部宣告的變數都是隱藏的,除非被明確導出,否則不可用於其他模組。

函數作用域表示函數中定義的參數和變數在函數的任意位置都可見

letconst 宣告的變數具有塊作用域。 var 沒有區塊作用域。

【相關推薦:JavaScript影片教學

#

以上是JavaScript作用域的全面解析(附程式碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除