6.1作用域
通常來說,一段程序代碼中所用到的名字並不總是有效和可用的,而限定這個名字的可用性的代碼範圍就是這個*名字的作用域。作用域的使用提高了程序邏輯的局部性,增強了程序的可靠性,減少了名字衝突。
JavaScript(es6前)中的作用域有兩種:
- 全局作用域
- 局部作用域(函數作用域)
6.1.2 全局作用域
作用於所有代碼執行的環境(整個 script 標籤內部)或者一個獨立的 js 文件。
6.1.3 局部作用域
作用於函數內的代碼環境,就是局部作用域。 因爲跟函數有關係,所以也稱爲函數作用域。
6.1.4 JS沒有塊級作用域
塊作用域由 { } 包括。
-
在其他編程語言中(如 java、c#等),在 if 語句、循環語句中創建的變量,僅僅只能在本 if 語句、本循環語句中使用,如下面的Java代碼:
java有塊級作用域:
if(true){ int num = 123; system.out.print(num); // 123 } system.out.print(num); // 報錯
以上java代碼會報錯,是因爲代碼中 { } 即一塊作用域,其中聲明的變量 num,在 “{ }” 之外不能使用;
而與之類似的JavaScript代碼,則不會報錯:
Js中沒有塊級作用域(在ES6之前)
if(true){ var num = 123; console.log(123); //123 } console.log(123); //123
6.2變量的作用域
6.2.1 全局變量
在JavaScript中,根據作用域的不同,變量可以分爲兩種:
- 全局變量
- 局部變量
6.2.2 局部變量
在全局作用域下聲明的變量叫做全局變量(在函數外部定義的變量)。
- 全局變量在代碼的任何位置都可以使用
- 在全局作用域下 var 聲明的變量 是全局變量
- 特殊情況下,在函數內不使用 var 聲明的變量也是全局變量(不建議使用)
6.2.3 全局變量和局部變量的區別
- 全局變量:在任何一個地方都可以使用,只有在瀏覽器關閉時纔會被銷燬,因此比較佔內存
- 局部變量:只在函數內部使用,當其所在的代碼塊被執行時,會被初始化;當代碼塊運行結束後,就會被銷燬,因此更節省內存空間
6.3作用域鏈
6.3.1 什麼是作用域鏈
只要是代碼都一個作用域中,寫在函數內部的局部作用域,未寫在任何函數內部即在全局作用域中;如果函數中還有函數,那麼在這個作用域中就又可以誕生一個作用域;根據在[內部函數可以訪問外部函數變量]的這種機制,用鏈式查找決定哪些數據能被內部函數訪問,就稱作作用域鏈
案例分析1:
function f1() {
var num = 123;
function f2() {
console.log( num );
}
f2();
}
var num = 456;
f1();
作用域鏈:採取就近原則的方式來查找變量最終的值。
6.3.2 作用域鏈查找機制
var a = 1;
function fn1() {
var a = 2;
var b = '22';
fn2();
function fn2() {
var a = 3;
fn3();
function fn3() {
var a = 4;
console.log(a); //a的值 ?
console.log(b); //b的值 ?
}
}
}
fn1();
6.4預解析
6.4.1 預解析相關概念
JavaScript 代碼是由瀏覽器中的 JavaScript 解析器來執行的。JavaScript 解析器在運行 JavaScript 代碼的時候分爲兩步:預解析和代碼執行。
預解析:在當前作用域下, JS 代碼執行之前,瀏覽器會默認把帶有 var 和 function 聲明的變量在內存中進行提前聲明或者定義。
-
代碼執行: 從上到下執行JS語句。
預解析會把變量和函數的聲明在代碼執行之前執行完成
6.4.2 變量預解析
預解析也叫做變量、函數提升。
變量提升(變量預解析): 變量的聲明會被提升到當前作用域的最上面,變量的賦值不會提升。
console.log(num); // 結果是多少?
var num = 10; // ?
結果:undefined
注意:**變量提升只提升聲明,不提升賦值**
6.4.3 函數預解析
函數提升: 函數的聲明會被提升到當前作用域的最上面,但是不會調用函數。
fn();
function fn() {
console.log('打印');
}
結果:控制檯打印字符串 --- ”打印“
注意:函數聲明代表函數整體,所以函數提升後,函數名代表整個函數,但是函數並沒有被調用!
6.4.4 函數表達式聲明函數問題
函數表達式創建函數,會執行變量提升,此時接收函數的變量名無法正確的調用:
fn();
var fn = function() {
console.log('想不到吧');
}
結果:報錯提示 ”fn is not a function"
解釋:該段代碼執行之前,會做變量聲明提升,fn在提升之後的值是undefined;而fn調用是在fn被賦值爲函數體之前,此時fn的值是undefined,所以無法正確調用
6.4.5 預解析規則總結
預解析過程
- 把var聲明的變量提升到當前作用域最前面,不會提升賦值 var num = 19 ; var fn = function() {..}
- 把函數聲明 提升到當前作用域的最前面,, function test() {....}
- 如果函數同名 後者會覆蓋前者
- 如果 var聲明的 和 函數聲明的同名 , 函數覆蓋var聲明
6.4.6 案例分析
var a = 4;
console.log(a);
a = 6;
console.log(a);
function a() {
console.log('哈');
}
a();
a = 10;
console.log(a); 預解析後: 函數與變量聲明重複,函數會覆蓋掉變量聲明,底下的a=4又覆蓋掉了函數 所以執行a() 的時候會報錯 底部就不執行了
function a() {
console.log('哈');
}
var a;
a = 4;
console.log(a); //4
a = 6;
console.log(a); // 6
a(); //報錯
a = 10;
console.log(a); //不執行