作用域:變量可以起作用的範圍和區域
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代碼的時候,分爲兩個過程:預解析(編譯)過程和代碼執行過程
預解析過程:
語法檢查,如果有錯誤,直接停止後續步驟不再運行。
把變量和函數的聲明提升到當前作用域的最前面,只會提升聲明,不會提升賦值和調用。
先提升變量後提升函數,如果函數和變量同名,則被替換;
代碼執行過程
變量的賦值,函數的調用,循環判斷等,根據代碼由上往下順序執行;
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); //報錯
預解析中 , 變量的提升 , 只會在當前的作用域中提升 , 提前到當前的作用域的最上面
函數中的變量只會提前到函數的作用域的最前面 , 不會出去