作用域
詞法作用域
作用域
域表示的就是范圍,即作用范圍
- 就是一個名字在什么地方能使用,在什么地方不能使用
塊級作用域
塊級別的作用范圍
// 在 c , java 等編程語言中,下面的語法報錯
{
var num = 123; // 應該用int , 這里是偽代碼
{
console.log(num); // 123
}
console.log(num); // 報錯
}
在 js 中采取詞法作用域
詞法(代碼)作用域,就是代碼在編寫過程中體現出來的作用范圍,代碼一旦寫好,不用執行,他的作用范圍就已經確定好了,這個就是所謂的詞法作用域
在 js 中的詞法作用域規則
1. 函數允許方位函數外的數據
2. 整個代碼結構中只有函數可以限定作用域
3. 作用規則首先是提升規則分析
4. 就近原則如果當前作用規則有了名字,就不考慮外面的名字
在 js 中作用域分析方法
1. 先進行預解析,分析預解析過程
* 程序在執行過程, 會先將代碼讀取到內存中檢查. 會將所有的聲明在此時進行標記. 所謂的標記就是
讓 js 解釋器知道有這個名字, 后面在使用名字的時候, 不會出現未定義的錯誤. 這個標記過程就是提升.
* 聲明
1. 名字的聲明, 標識符的聲明( 變量名聲明 )
* 名字的聲明就是讓我們的解釋器知道有這個名字
* 名字沒有任何數據與之對應
2. 函數的聲明
* 函數聲明包含兩部分
* 函數聲明與函數表達式有區別, 函數聲明是單獨寫在一個結構中, 不存在任何語句, 邏輯判斷等結構中
* 首先函數聲明告訴解釋器有這個名字存在. 該階段與名字聲明一樣
* 告訴解釋器, 這個名字對應的函數體是什么**(函數名和函數體綁定鏈接)**
2. 再進行代碼執行過程
常見的簡單作用域問題
例子 1:
var num = 123;
function foo(){
console.log(num);
}
foo(); // 輸出 123
分析
- 預解析
變量num變量名提升 函數foo函數名提升
- 代碼執行
num賦值123,函數內區域為獨立區域,可以使用外部數據,但是現在變量名相同,覆蓋外面的值 函數foo執行 函數區域中變量num變量名提升 輸出num 為undefined num賦值456 輸出num 為456
例子 2:
if(false){
var num = 123;
}
console.log(num); // 輸出undefined
分析
- 預解析
變量名num提升
- 執行代碼
if判斷為false,不進入 輸出num,num定義未賦值,為undefined
例子 3:
var num = 123;
function foo(){
var num = 456;
function fn(){
console.log(num); // 輸出456
};
fn();
}
foo();
分析
- 預解析
變量名num和函數名foo聲明提升,函數名foo和函數體鏈接
- 執行代碼
num = 123, 賦值 執行函數, 函數內部聲明提升,變量名num和函數名fn 變量num=456, 執行函數fn 輸出num, fn函數內部沒有num,向上級尋找 num=456, 輸出num
例子 4:
var num = 123;
function foo1(){
var num = 456;
function foo2(){
num = 789;
function foo3(){
console.log(num); // 輸出789
}
foo3();
}
foo2();
}
foo1(); // 輸出456
console.log(num); // 輸出123
分析
- 預解析
變量名num和函數名foo1聲明提升,函數名foo1和函數體綁定
- 執行代碼
num = 123, 賦值 執行函數foo1, 變量名num和函數名foo2聲明提升, 函數名foo2和函數體綁定 num = 456, 賦值 執行函數foo2, num在foo2函數中沒有, 訪問上級foo1得到num, num = 789, 賦值函數名foo3聲明提升並且綁定函數體 執行函數foo3, 輸出num, foo3中沒有num,訪問上級,得到num = 789 輸出num為789 跳出函數, 輸出num為123
例子 5:
if ( ! 'a' in window ) {
var a = 123;
}
console.log( a ); // undefined
分析
- 預解析
變量a聲明提升
- 執行代碼
判斷window中是否存在a,a存在 判斷為false,不執行if中的代碼 輸出a,為undefined
復雜的作用域問題
例子 1
if ( true ) {
function f1 () {
console.log( 'true' );
}
} else {
function f1 () {
console.log( 'false' );
}
}
f1();
分析
新版瀏覽器
- 預解析
無,函數f1被瀏覽器認為是函數表達式,不進行變量名提升
- 執行代碼
if判斷進入true 執行函數表達式f1 輸出結果為true
舊版瀏覽器
- 預解析
函數f1變量聲明提升,f1函數名和最后一個函數體連接在一起
- 執行代碼
if判斷進入true 執行f1 輸出false
例子 2
if ( false ) {
function f1 () {
console.log( 'true' );
}
} else {
function f1 () {
console.log( 'false' );
}
}
f1();
分析
新版瀏覽器
- 預解析
無,f1為函數表達式,沒有進行聲明提升
- 執行代碼
if判斷 執行f1 輸出false
舊版瀏覽器
- 預解析
函數名f1,聲明提升,函數名和最后一個函數體鏈接在一起
- 執行代碼
if判斷 執行f1 輸出false
例子 3
var num = 123;
function f1() {
console.log( num );
}
function f2() {
var num = 456;
f1();
}
f2();
分析
- 預解析
聲明提升,變量num,函數名f1,f2,函數名和函數體鏈接
- 執行代碼
num = 123 執行函數f2 變量num賦值456 執行函數f1 輸出num,函數里面沒有,向上尋找,上級為(0級作用域鏈),num=123,輸出num為123
例子 4
var num = 123;
function f1() {
console.log( num );
}
function f2() {
num = 456;
f1(num);
}
f2();
分析
- 預解析
變量名num,函數名f1,f2聲明提升,函數名和函數體鏈接一起
- 代碼執行
num=123 執行函數f2 num=456,f2中沒有num,向上級尋找得到num為123,將num = 123賦值,num = 456 執行f1, f1中沒有num, 向上級尋找得到num為456 輸出num,num=456,輸出num456