Js代碼分爲兩個階段:編譯階段和執行階段javascript
Js代碼的編譯階段會找到全部的聲明,並用合適的做用域將它們關聯起來,這是詞法做用域的核心內容java
包括變量聲明(var a)和函數聲明(function a(){})在內的全部聲明都會在代碼被執行前的預編譯階段首先被處理瀏覽器
過程就好像變量聲明和函數聲明從他們代碼中出現的位置被移動到執行環境的頂部,這個過程就叫作提高函數
只有聲明操做會被提高,賦值和邏輯操做會被留在原地等待執行code
Js編譯器會把變量聲明當作兩個部分分別是聲明操做(var a)和賦值操做(a=2)ip
聲明操做在預編譯階段進行,聲明操做會被提高到執行環境的頂部,值是undefined(表示未初始化)作用域
賦值操做會被留在原地等待執行階段編譯器
1 var a = 2; 2 3 function foo() { 4 console.log(a); //undefined 5 var a = 10; 6 console.log(a); //10 7 } 8 9 foo(); 10 11 // 至關於 12 13 var a = 2; 14 15 function foo() { 16 var a; 17 console.log(a); //undefined 18 a = 10; 19 console.log(a); //10 20 } 21 22 foo();
定義函數有兩種方式:函數聲明和函數表達式io
函數聲明提高會在預編譯階段把聲明和函數體總體都提早到執行環境頂部,因此咱們能夠在函數聲明以前調用這個函數console
函數表達式,其實就是變量聲明的一種,聲明操做會被提高到執行環境頂部,並賦值undefined。賦值操做被留在原地等到執行。
1 // 函數聲明 2 3 foo(); //100 4 5 function foo(){ 6 console.log(100); 7 }
1 // 函數表達式 2 baz(); // TypeError: baz is not a function 3 4 var baz = function(){ 5 console.log(200); 6 } 7 8 //至關於 9 10 var baz; 11 12 baz(); 13 14 baz = function() { 15 console.log(200); 16 };
Js中使用函數級做用域,不存在塊級做用域。全部普通塊中的聲明都會被提高到頂部,因此控制語句對聲明的控制就顯得徹底沒有效果
1 if (false) { 2 var a = 10; 3 } 4 5 console.log(a); //undefined 6 7 // 至關於 8 9 var a; 10 if (false) { 11 a = 10; 12 } 13 14 console.log(a) //undefined
1 console.log(a); //undefined 2 3 if (false) { 4 function a() { 5 console.log(100); 6 } 7 } 8 9 a(); //TypeError: a is not a function 理論上應該是100
奇怪吧??函數提高發生在全部代碼執行以前,因此儘管a函數的定義過程寫在了if分支中,可是理論上,它是不會影響函數聲明提高的
在新版本的瀏覽器中會出現此問題,舊版本的瀏覽器中會在控制檯中打印出100
這也提醒了咱們儘可能不要在控制語句中進行聲明,會形成不少沒法預知的bug
提高操做會優先進行函數的聲明
函數會首先被提高而後纔是變量,重複的變量聲明會被忽略,只剩下賦值操做,多個函數聲明能夠進行覆蓋
聲明的順序是這樣的:
找到全部的函數聲明,初始化函數體,若有同名的函數則會進行覆蓋
查找變量聲明,初始化爲undefined,若是已經存在同名的變量,就什麼也不作直接略過
1 // 1 2 foo(); //200 3 4 function foo() { 5 console.log(100); 6 } 7 8 function foo() { 9 console.log(200); 10 } 11 12 // 2 13 console.log(foo); //function foo(){...} 14 15 function foo(){ 16 console.log(200); 17 } 18 var foo = 100;