翻譯:瘋狂的技術宅
https://medium.freecodecamp.o...-javascript-cbd957022652
本文首發微信公衆號:jingchengyideng
歡迎關注,天天都給你推送新鮮的前端技術文章javascript
做用域決定了變量的生命週期和可見性,變量在做用域範圍以外是不可見的。前端
JavaScript 的做用域包括:模塊做用域,函數做用域,塊做用域,詞法做用域和全局做用域。java
在任何函數、塊或模塊範圍以外定義的變量具備全局做用域。能夠在程序的任意位置訪問全局變量。程序員
當啓用模塊系統時,建立全局變量會變得困難,但仍然能夠作到這一點。能夠在 HTML 中定義一個變量,這個變量須要在函數以外聲明,這樣就能夠建立一個全局變量:面試
<script> let GLOBAL_DATA = { value : 1}; </script> console.log(GLOBAL_DATA);
當沒有模塊系統時,建立全局變量會容易不少。在任何文件中的函數外聲明的變量都是全局變量。segmentfault
全局變量貫穿於程序的整個生命週期。微信
另外一種建立全局變量的方法是在程序的任意位置使用 window
全局對象:閉包
window.GLOBAL_DATA = { value: 1 };
這樣 GLOBAL_DATA
變量會隨處可見。異步
console.log(GLOBAL_DATA)
不過你也知道這種作法是很差的。ide
若是不啓用模塊,在全部函數以外聲明的變量是全局變量。在模塊中,在函數外部聲明的變量都是隱藏的,除非顯式導出,不然不可用於其餘模塊。
導出使函數或對象可用於其餘模塊。在這個例子中,我從模塊文件 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();
用 let
或 const
聲明的變量不能在同一做用域內從新聲明:
function doSomething(){ let x = 1 let x = 2; } //Uncaught SyntaxError: Identifier 'x' has already been declared
也許咱們能夠沒必要關心這一點,由於 var
已經開始變得過期了。
塊做用域用花括號定義。它由 {
和 }
分隔。
用 let
和 const
聲明的變量能夠受到塊做用域的約束,只能在定義它們的塊中訪問。
思考下面這段關於 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
變量。還能夠從外部函數訪問變量 x1
和 x2
和全局變量 x0
。
若是找不到變量,它將在嚴格模式下返回錯誤。
"use strict"; x = 1; console.log(x) //Uncaught ReferenceError: x is not defined
非嚴格模式也被稱爲「草率模式」,它會草率的建立一個全局變量。
x = 1; console.log(x); //1
在全局做用域中定義的變量可在程序的任何位置使用。
在模塊中,在函數外部聲明的變量都是隱藏的,除非被顯式導出,不然不可用於其餘模塊。
函數做用域意味着函數中定義的參數和變量在函數的任意位置均可見
用 let
和 const
聲明的變量具備塊做用域。 var
沒有塊做用域。