1.1什麼是函數提升和變數的提升? JS引擎在運行整個JS代碼的過程中,分為倆步。 第一步是讀取和解析JS代碼,第二部是執行。 在引擎解析JS代碼的時候,當解析器遇見變數聲明(var 變數名)和函數聲明 (function 函數名)的時候,會將這些聲明提到各自作用域的最前面。 1.2 作用域 在ES ...
1.1什麼是函數提升和變數的提升?
JS引擎在運行整個JS代碼的過程中,分為倆步。
第一步是讀取和解析JS代碼,第二部是執行。
在引擎解析JS代碼的時候,當解析器遇見變數聲明(var 變數名)和函數聲明
(function 函數名)的時候,會將這些聲明提到各自作用域的最前面。
1.2 作用域
在ES6之前,JS是沒有塊級作用域的。只有2種作用域:
- 1.全局作用域
- 2.函數作用域
註:在其他語言中,{...}被稱為代碼塊,所形成的作用域被稱作塊級作用域,如:
if(...){
var a = 1;
}
for(var i = 0;i<5;i++){
var b = 1;
}
console.log(a);
console.log(b);
console.log(i);
如果以上倆例子有塊級作用域的話,a,b,i是不能被訪問到的,但是
在JS中是可以的(ES6之前),所以沒有塊級作用域。
2.1 變數提升
2.1.1 聲明變數
首先我們聲明變數是通過var關鍵字,如果不用var直接賦值的話會被解析器
當作是全局變數。但是切記,變數的提升只限於用var聲明的變數,沒通過
var聲明的變數將不會被提升。看下邊的例子:
var a = 1;
console.log(a);// 1
// 輸出的結果為1,沒問題,因為代碼在執行的時候是按順序執行的。
// 解析的時候,經過變數提升後的結果如下:
var a;
a = 1;
console.log(a);// 1
console.log(a);// undefined
var a = 1;
// 這次的結果輸出為undefined,我們都知道,當一個變數聲明瞭卻未賦值的時候就會出現undefined,
// 但是一個變數沒有聲明的話,就會輸出 a is not defined,先看本例經變數提升後的結果:
var a ;
console.log(a);// undefined
a = 1;
// 所以由此可見,變數的提升只會提升變數的聲明,而不會提升變數的賦值。
console.log(a);// a is not defined
a = 1;
// 當我們把var去掉的時候,結果變成了 a is not defined,很明顯,沒有var聲明的變數,沒有被提升至作用域的最前邊,也就是變數提升只是對用var聲明的變數而言的。
- 函數的內部也是如此
(function(){
console.log(a);//undefined
var a = 1;
})()
提升結果如下:
(function(){
var a
console.log(a);//undefined
a = 1;
})()
2.1.2 所以暫時我們可以總結一下變數提升:
- 只有用var聲明的變數才存在變數提升這一說法
- 變數提升只提升變數的聲明,不會提升賦值這一部分;
3.1 函數提升
3.1.1函數的聲明
函數的聲明有倆種方式,一種為:
function fn(){
}
//另一種為:
var fn = function(){
}
// 首先的我們得知道函數屬於引用類型,函數名實際上相當於一個指針,保存的是函數體所在的地址,所以函數也可以通過函數表達式var fn來聲明,但是同為函數,他們的提升結果卻是不同的。
3.1.2 函數式聲明
function fn(){
console.log('hello');
}
fn();// hello
// 結果輸出為hello,這個不難理解,代碼的順序執行。函數提升後的結果還是這樣。
fn();// hello
function fn() {
console.log('hello');
}
// 執行結果還是hello,因為函數在解析代碼的時候,同樣,函數的聲明被提到了作用域的最前邊,如下:
function fn(){
console.log('hello');
}
fn();// hello
// 需要註意的是整個function fn(){...}均為函數的定義(聲明)。
var fn = function(){
console.log('hello');
}
fn();//hello
// 用var聲明的函數同變數一樣,先把定義提升至最前邊,如下:
var fn;
fn = function(){
console.log('hello');
}
fn();//hello
fn();//fn is not a function
var fn = function(){
console.log('hello');
}
// 但是將用var聲明的函數放在後邊就不行了,因為這樣聲明的函數,提升後是下邊這樣的:
var fn;
fn();//fn is not a function
fn = function(){
console.log('hello');
}
// 同變數提升一樣,提升的只是定義,並沒有賦值。
4 綜合運用
console.log(a);
function a() { //定義函數
}
console.log(a);
var a = 3; //變數
console.log(a);
運行結果如下:
function a() {
}
function a() {
}
3
console.log(a);
var a = 3;
console.log(a);
function a() {
}
console.log(a);
由此可見
倆次舉例中,函數和變數是同名的,在代碼未執行到變數賦值語句的時候,console列印出來的均為函數,變數賦值以後,列印的才是剛剛賦的值,所以由綜合例子可以得出:
當變數和函數同名時,函數的優先順序高!
總結
由以上的例子不難看出變數提升和函數提升的特點,可以總結如下:
- 所有的聲明都會提升到各自作用域的最頂上去。
- 只有用var聲明的變數才存在變數提升這一說法
- 變數提升只提升變數的聲明,不會提升賦值這一部分;
- 同一個變數只會聲明一次,其他的會被忽略掉。
- 函數聲明的優先順序高於變數聲明的優先順序
- 所有變數的聲明,在函數內部第一行代碼開始執行的時候就已經完成。