1. 變量的作用域
全局變量
定義:在函數外聲明的變量
作用范圍:在整個JS文檔中生效
生命周期:頁面關閉時銷毀
局部變量
定義:在函數內用?
var
?聲明的變量作用范圍:只能在函數內部使用
生命周期:函數執行完畢時銷毀
作用域規則
1.1 全局變局部
全局變量可以在函數內部的任意位置使用
1.2 局部變全局
局部變量不能在全局使用
兩種方法在全局使用局部變量:
去除?
var
?關鍵字(隱式全局變量,不推薦)使用?
return
?返回變量值
塊級作用域(ES6新增)
let
?和?const
?聲明的變量具有塊級作用域只在聲明它們的?
{}
?內有效
2. 作用域鏈
概念
函數中如果在當前作用域中沒有查到值,就會向上級作用域去查
直到查到全局作用域,這樣形成的鏈條就叫做作用域鏈
特點
內部環境可以通過作用域鏈訪問所有外部環境
外部環境不能訪問內部環境的任何變量和函數
示例分析
var n = 10;
function outer(){function inner(){function center(){console.log(n); // 輸出 undefined}center();}inner();var n = 15; // 變量提升,但賦值在console之后
}
outer();
3. 預解析(變量提升)
執行過程
預解析階段:提前加載所有?
var
?和?function
代碼執行階段:按書寫順序執行
預解析規則
var
:只聲明不定義(值為?undefined
)function
:既聲明又定義(包含函數體)函數優先級高于變量
預解析只發生在當前作用域下
優先級順序
函數聲明(最高優先級)
變量聲明
代碼執行
4. 案例分析
案例1
var num = 10;
fun();
function fun(){console.log(num); // undefinedvar num = 20;
}
案例2
var num = 10;
function fun() {console.log(num); // undefinedvar num = 20;console.log(num); // 20
}
fun();
案例3
var a = 18;
f1();
function f1() {var b = 9;console.log(a); // undefinedconsole.log(b); // 9var a = 13;
}
案例4
console.log(a, b, c); // undefined undefined undefined
var a = b = c = 5; // b和c成為隱式全局變量
案例5
f1();
console.log(c); // 9
console.log(b); // 9
console.log(a); // 報錯
function f1() {var a = b = c = 9; // 只有a是局部變量console.log(a); // 9console.log(b); // 9console.log(c); // 9
}
案例6
fn(); // 報錯
var fn = function(){console.log(11);
}
案例7
function fn(){console.log(a); // 報錯a = 100;
}
fn();
console.log(a);
案例8
console.log(show); // function show
function show(){console.log(123);
}
var show = 10;
案例9
function show(){console.log(123);
}
var show = 10;
console.log(show); // 10
5. 重要概念總結
變量提升等價代碼
// 原始代碼
console.log(a);
var a = 'hello';
function a(){console.log(123);
}// 提升后等價代碼
function a(){console.log(123);
}
// var a; // 被忽略
console.log(a); // function a
a = 'hello';
作用域鏈案例
var a = 1
function fn1(){function fn2(){console.log(a) // 輸出2}function fn3(){var a = 4fn2()}var a = 2return fn3
}
var fn = fn1()
fn()
6. 最佳實踐
使用?
let
/const
?代替?var
避免隱式全局變量
函數優先使用函數表達式
啟用嚴格模式?
"use strict"
變量聲明放在作用域頂部