1 function log(str) { 2 // 本篇文章全部的打印都將調用此方法 3 console.log(str); 4 }
函數聲明和變量聲明老是會被解釋器悄悄地被「提高」到方法體的最頂部瀏覽器
變量聲明、命名、提高函數
在JS中, 變量有4種基本方式進入做用域:this
變量提高spa
function test1() { a = 5; log(a); log(window.a); var a = 10; log(a); } test1();
依次會輸出 5 、undefined 、10 由於在解析時候是等價於code
1 var a; 2 a=5; 3 log(a); 4 log(window.a); 5 a=10; 6 log(a);
接着看另一個例子:blog
1 function test2() { 2 var a = 1; 3 var b = 2; 4 var c = 3; 5 } 6 /*test2中的語句,是這樣被執行的 這個時候就把變量提高了 7
8 function test2(){ 9 var a,b,c; 10 var a = 1; 11 var b = 2; 12 var c = 3; 13 } 14 */
只有函數級做用域,if語句不會有:test3():ip
function test3(){
var a = 1;
log(a); // 1
if (true) {
var a = 2;
log(a); //2
}
log(a); // 2
}
函數的提高作用域
咱們寫JS的時候,一般會有兩種寫法:文檔
咱們須要重點注意的是,只有函數聲明形式才能被提高。
變量賦值並無被提高,只是聲明被提高了。可是,函數的聲明有點不同,函數體也會一同被提高io
1 function test3() { 2 fn(); 3 function fn() { 4 log("我來自fn"); 5 } 6 } 7 test3(); 8 function test4() { 9 fn(); // fn is not a function 10 var fn = function fn() { 11 alert("我來自 fn test4"); 12 } 13 } 14 test4();
函數表達式須要注意的
1 function test5() { 2 var fn = function fn1() { 3 log(fn === fn1); // true 4 log(fn == fn1); // true 5 } 6 fn(); 7 log(fn === fn1); // fn1 is not defined 8 log(fn == fn1); // fn1 is not defined 9 } 10 test5();
!兼容
// b();
// var a = function b() {alert('this is b')};
// 則ie下是能夠執行b的. 說明不一樣瀏覽器在處理函數表達式細節上是有差異的.
補充一點函數表達式
定義裏面的指定的函數名是否是被提高的
1 function text7() { 2 a(); // TypeError "a is not a function" 3 b(); 4 c(); // TypeError "c is not a function" 5 d(); // ReferenceError "d is not defined" 6 7 var a = function() {}; // a指向匿名函數 8 function b() {}; // 函數聲明 9 var c = function d() {}; // 命名函數,只有c被提高,d不會被提高。 10 11 a(); 13 b(); 14 c(); 15 d(); // ReferenceError "d is not defined" 16 } 17 text7();
你們先看下面一段代碼test6,思考一下會打印什麼?
1 function text6() { 2 var a = 1; 3 function b() { 4 a = 10; 5 return; 6 function a() {} 7 } 8 b(); 9 log(a); // ? 10 } 11 text6();
||
||
||
|| 輸出在下面
||
||
||
||
||
||
what? 什麼鬼?爲何是1?
這裏須要注意的是,在function b()中,
var = a // function 類型的
a=10; // 從新把10複製給a, 此時的a是function b()中的內部變量
return;
function a() {} // 不會被執行
因此,外面輸出的a 依舊是最開始定義的全局變量
函數的聲明比變量的聲明的優先級要高
1 function text6() { 2 function a() {} 3 var a; 4 log(a); //打印出a的函數體 5 6 var b; 7 function b() {} 8 log(b); //打印出b的函數體 9 10 // !注意看,一旦變量被賦值後,將會輸出變量 11 var c = 12 12 function c() {} 13 log(c); //12 14 15 function d() {} 16 var d = 12 17 log(d); //12 18 } 19 text6();
變量解析的順序
通常狀況下,會按照最開始說的四種方式依次解析
也有例外:
CAO!這麼多坑,之後腫麼寫代碼?
用var定義變量。對於一個名稱,在一個做用域裏面永遠只有一次var聲明。這樣就不會遇到做用域和變量提高問題。
ECMAScript參考文檔關於做用域和變量提高的部分:
若是變量在函數體類聲明,則它是函數做用域。不然,它是全局做用域(做爲global的屬性)。變量將會在執行進入做用域的時候被建立。塊(好比if(){})不會定義新的做用域,只有函數聲明和全局性質的代碼(單個JS文件)纔會創造新的做用域。變量在建立的時候會被初始化爲undefined。若是變量聲明語句裏面帶有賦值操做,則賦值操做只有被執行到的時候纔會發生,而不是建立的時候。