提升(Hoisting)
其實主要是為了釐清 JavaScript 的運作所提出的觀念,在 ECMAScript 並未提出這個專有名詞,而所謂的提升,就是先將函式
與變數宣告
先提到程式碼最上面的動作,再接著執行程式碼
JavaScript 會在執行環境中運行程式碼,而在建立一個執行環境時可以分為兩個階段:
undefined
在創造環境階段優先把函式與變數的記憶體空間準備好,就叫做
提升 Hositing
同上面所述,在創造階段時,會將程式碼中所有變數先提到最前面,並在記憶體中預留空間給變數後,系統會先給予一個 undefined
的初始值,等到了執行階段,會依序執行剩下的程式碼,這時候才會賦予變數值,範例如下:
var nickName = 'Carol';
console.log(nickName); // Carol
// 1. 創造階段
// 先宣告變數 nickName,在記憶體中預留空間,預設值為 undefined
var nickName;
// 2. 執行階段
// 賦予變數 nickName 值
nickName = 'Carol';
// 顯示變數 nickName 值
console.log(nickName); // Carol
第一個 console.log(nickName);
在創造階段中,記憶體已預留空間給 變數 nickName
,但還未賦予值,系統先給予一個預設值 undefined
,所以顯示 undefined
第二個 console.log(nickName);
到了執行階段,程式碼會依序執行,所以到了第二個 console.log
,已賦予 變數 nickName
一個值,顯示 Carol
console.log(nickName); // undefined
var nickName = 'Carol';
console.log(nickName); // Carol
// 1. 創造階段
// 先宣告變數 nickName,在記憶體中預留空間,預設值為 undefined
var nickName;
// 2. 執行階段
// 顯示變數 nickName 值
console.log(nickName); // undefined
// 賦予變數 nickName 值
nickName = 'Carol';
// 顯示變數 nickName 值
console.log(nickName); // 'Carol'
因 變數 nickName
沒被建立或是宣告,所以系統找不到此變數,故顯示錯誤訊息 Uncaught ReferenceError: nickName is not defined
console.log(nickName);
// Uncaught ReferenceError: nickName is not defined
undefined
與is not defined
差異可在 [Day6] 'undefined' vs 'not defined' 查看更多內容
關於函式的提升,首先我們要了解函式陳述式與函式表達式
函式陳述式又稱為具名函式,他會直接給函式一個名子,並可直接利用此名子呼叫函式,而在創造階段時,函式陳述式優先,他會在記憶體中將函式陳述式的所有內容做保留
function fn() {
// ...
}
fn();
// 1.創造階段
function fn() {
// ...
}
// 2.執行階段
fn();
fn();
var nickName = 'Carol';
function fn() {
console.log('我的暱稱叫 Carol');
}
var nickName = 'Mary';
fn();
// 1.創造階段
function fn() {
console.log('我的暱稱叫 Carol');
}
// 2.執行階段
fn();
函式表達式可以將匿名函式或具名函式賦予至一個變數上。而函式表達式並不會像函式陳述式一樣,在創造階段就已經被記憶體保留空間,只會在創造階段時將此變數在記憶體中保留空間,直到執行階段才會將函式表達式賦予給此變數,所以若要利用函式表達式,就必須等函式已經賦予到變數上,才能運行此函式,因此只有函式陳述式會提升,而函式表達式則不會
若未定義函式名稱則為匿名函式,反之則為具名函式
var fn = function () {
// ...
}
fn();
// 1.創造階段
var fn;
// 2.執行階段
fn = function () {
// ...
}
fn();
console.log(fn); // undefined
var fn = function () {
console.log('我的暱稱叫 Carol');
}
console.log(fn); // ƒ () { console.log('我的暱稱叫 Carol'); }
fn(); // 我的暱稱叫 Carol
// 1.創造階段
var fn;
// 2.執行階段
console.log(fn); // undefined
fn = function () {
console.log('我的暱稱叫 Carol');
}
console.log(fn); // ƒ () { console.log('我的暱稱叫 Carol'); }
fn(); // 我的暱稱叫 Carol
因匿名函式還未賦予到變數上,所以還不能運行此函式,故會報錯
fn();
var fn = function () {
console.log('我的暱稱叫 Carol');
}
// Uncaught TypeErroe: fn2 is not a function
// 1.創造階段
var fn;
// 2.執行階段
fn();
// Uncaught TypeErroe: fn2 is not a function
// 報錯,所以下面的程式碼不會繼續執行
// fn2 = function () {
// console.log('我的暱稱叫 Carol');
// }
fn(); // 第 1 名
function fn() {
console.log('第 1 名');
}
fn(); // 第 1 名
var fn = function () {
console.log('第 2 名');
}
fn(); // 第 2 名
// 1.創造階段
function fn() {
console.log('第 1 名');
}
var fn;
// 2.執行階段
fn(); // 第 1 名
fn(); // 第 1 名
fn = function () {
console.log('第 2 名');
}
fn(); // 第 2 名
fn(); // 第 1 名
var fn = function () {
console.log('第 2 名');
}
fn(); // 第 2 名
function fn() {
console.log('第 1 名');
}
fn(); // 第 2 名
// 1.創造階段
function fn() {
console.log('第 1 名');
}
var fn;
// 2.執行階段
fn(); // 第 1 名
fn = function () {
console.log('第 2 名');
}
fn(); // 第 2 名
fn(); // 第 2 名
後面函式覆蓋掉前面
fn(); // 第 2 名
function fn() {
console.log('第 1 名');
}
fn(); // 第 2 名
function fn() {
console.log('第 2 名');
}
fn(); // 第 2 名
// 1.創造階段
function fn() {
console.log('第 1 名');
}
function fn() {
console.log('第 2 名');
}
// 2.執行階段
fn(); // 第 2 名
fn(); // 第 2 名
fn(); // 第 2 名
fn(); // undefined
function fn() {
console.log(nickName);
}
var nickName = 'Carol';
fn(); // Carol
// 1.創造階段
function fn() {
console.log(nickName);
}
var nickName;
// 2.執行階段
fn(); // undefined
nickName = 'Carol';
fn(); // Carol
fn();
function fn() {
if (rank) {
rank = 2;
}
}
console.log(rank); // undefined
var rank = 1;
console.log(rank); // 1
fn();
console.log(rank); // 2
// 1.創造階段
function fn() {
if (rank) {
rank = 2;
}
}
var rank;
// 2.執行階段
fn();
console.log(rank); // undefined
rank = 1;
console.log(rank); // 1
fn();
console.log(rank); // 2
變數在宣告時,有一特性,如該變數已存在,則不會修正他 (只能用 var
,因 let
與 const
不能重複宣告)
var a = 1;
var a;
console.log(a); // 1