JS的預解析是指在程式碼執行之前,JavaScript引擎會先對程式碼進行一次掃描,將變數宣告和函數宣告提升到當前作用域的頂部,以便在程式碼執行時能夠正確地存取這些變數和函數。這個過程也被稱為「提升」。
具體來說,在預解析過程中,JavaScript引擎會將函數宣告和變數宣告提升到當前作用域的頂部,而不管它們實際上在程式碼中的位置。這意味著,無論變數和函數宣告出現在程式碼的哪個位置,它們在程式碼執行之前就已經被處理了,可以在程式碼中任何位置被存取。
需要注意的是,只有函數宣告和變數宣告才會被提升,而不是變數的賦值操作。如果變數宣告和賦值操作同時出現在同一行程式碼中,只有變數宣告會被提升到作用域頂部,而變數的值仍然會在程式碼執行時賦值。
預解析在JavaScript中的具體表現有以下幾點:
1. 函數宣告提升 :在預解析階段,JavaScript引擎會將函數宣告(而非函數表示式)提升到作用域頂部。這意味著,在程式碼中函數宣告可以出現在函數呼叫之後,甚至可以出現在函數內部,但仍然可以被正確識別和呼叫。注意, 函數宣告既提升也定義 。
例如,下面的程式碼中,函數 foo() 在預解析階段被提升到了作用域頂部,因此可以在函數 bar() 內部被正確呼叫:
bar(); // hello function bar() { foo(); // 可以呼叫函數 foo() function foo() { console.log('hello'); } }
2. 變數宣告提升 :在預解析階段,JavaScript引擎也會將變數宣告提升到作用域頂部,但是不會提升變數的賦值操作。這意味著,變數可以在宣告之前被使用,但是如果在宣告之前進行賦值操作,會得到undefined。
例如,下面的程式碼中,變數 x 在宣告之前被使用,但是在宣告之前進行賦值操作會得到undefined:
console.log(x); // undefined var x = 10;
3. 函數表示式不提升 :在預解析階段,JavaScript引擎不會將函數表示式提升到作用域頂部,只會提升變數宣告。因此,在使用函數表示式定義的函數之前,必須先定義變數。
例如,下面的程式碼中,函數 foo() 是通過函數表示式定義的,因此必須先定義變數 foo 才能呼叫該函數:
foo(); // Uncaught TypeError: foo is not a function var foo = function() { console.log('hello'); };
JS的預解析還有一些需要注意的細節,包括以下幾點:
1. 函數宣告和變數宣告的作用域 :在預解析階段,函數宣告和變數宣告的作用域是整個函數或全域性作用域。這意味著,在函數內部宣告的函數和變數可以在整個函數內部被存取,而在全域性作用域宣告的函數和變數可以在整個指令碼中被存取。
2. 函數宣告和變數宣告的優先順序 :在預解析階段,函數宣告的優先順序高於變數宣告。這意味著,如果同名函數和變數同時宣告,函數宣告會覆蓋變數宣告。
例如,下面的程式碼中,變數 x 和函數 x() 同名,但是在預解析階段,函數 x() 會覆蓋變數 x :
console.log(x); // function x() {...} var x = 10; function x() { console.log('hello'); }
3. 非全域性作用域的預解析 :在非全域性作用域(如函數作用域)中,預解析只會發生在該作用域內。這意味著,在函數內部宣告的函數和變數只會被提升到該函數作用域的頂部,而不會影響到其他函數或全域性作用域。
例如,下面的程式碼中,函數 bar() 內部的變數 x 只會在函數作用域內被提升到作用域頂部,而不會影響到全域性作用域或其他函數的作用域:
function foo() { bar(); function bar() { console.log(x); // undefined var x = 10; } } foo();
總的來說,預解析是JavaScript中的一個重要概念,可以幫助我們更好地理解變數和函數的作用域和生命週期。瞭解預解析的規則和特點,可以幫助我們編寫更加規範和可讀性更好的JavaScript程式碼。
當然,現在用了let、const和函數式表示式等,可以避免因為變數提升等帶來的小麻煩。