好處:在 func1 中的變數 可被 func2 使用,而 func1 的變數不會被外層讀取。
壞處:資料不會正常的被銷毀,而會一直留著。
執行的內部函式的方式會是 fun1()() 後方括弧表示呼叫內部函式,假如將函式賦予在另一個變數上,內部的變數就會存在內層,而隨著函式執行而不斷改變。
如同以下的範例:
function test() {
let a = 10
return function c(b) {
a = a - b
return a
}
}
let d = test()
d(1) // 9
d(1) // 8
d(1) // 7
那閉包的原理又是什麼呢?為什麼當中的變數會留著而記憶體不會被釋放呢?這時候就需要提到另一個東西了,叫做範圍鏈 (Scope Chain)
Execution Contexts (執行環境) 被建立時, Scope Chain、變數、 this 都被建立且初始化。
進入 function EC 的時候,範圍鏈的初始化會包含一個 Activation Object (AO) 以及 [[Scope]]
,function 的 [[Scope]]
是當被宣告時而決定的,也就是 scope chain 會是 [AO, [[Scope]] ]
。
當 function 被建立時,AO 被初始化,且會有一個 arguments 的屬性,而 AO 可以當作 VO 來使用,而 VO 就是前面所說的,在每個執行環境建立時都會有一個 Variable Object (VO),用於存放被宣告的變數及函式等。
var a = 1
function test() {
var b = 2
function test2() {
var c = 3
console.log(b)
console.log(a)
}
test2()
}
test()
用以上的程式碼作為示範,整體的執行堆會像以下所寫的部分:
test2 EC: {
AO: {
c:3
},
scopeChain: [test2EC.AO, test2.[[Scope]]]
// [test2EC.AO, test2EC.[[Scope]]] = [test2EC.AO, testEC.scopeChain] = [test2EC.AO, testEC.AO, globalEC.VO]
}
test EC: {
AO: {
b: 2,
test2: function
},
scopeChain: [testEC.AO, test.[[Scope]]]
// [testEC.AO, testEC.[[Scope]]] = [testEC.AO, globalEC.VO]
}
globalEC: {
VO: {
a: 1,
test: function
},
scopeChain: [globalEC.VO]
}
首先會初始化的是 Global EC
裡面會有一個 VO 並宣告變數 a = undefined
及 test = func
再來建立自己的 Scope Chain 也就是 GlobalEC.VO,最後賦值 a = 1
。
呼叫 test()
於是,test EC 被建立,並疊在 Global EC 上,形成執行堆,
由於是函式,因此建立的會是 AO
並宣告變數 b = undefined
及 test2 = func
建立 Scope Chain,函式的 Scope Chain 會是
testEC.AO, testEC.[[Scope]]
,
testEC.[[Scope]] = GlobalEC.scopeChain = GlobalEC.VO
,
也就是說,testEC.scopeChain = [testEC.AO, GlobalEC.VO]
,
之後賦值 b = 2
。
呼叫 test2()
依照同樣的步驟,test2EC.scopeChain
會是 test2EC.AO, [[Scope]]
等同於 test2EC.AO, testEC.scopeChain
再拆成 test2EC.AO, testEC.AO, globlaEC.scopeChain
最後變成 test2EC.AO, testEC.AO, globlaEC.VO
。
這樣就可以很清楚的看出來,test2()
執行後,log 出的資料會是 2 跟 1,用 let
或 const
也會是同樣的結果,因為範圍鏈的關係,所以內層的函式可以藉由外層的 VO/AO 而得到變數。
function test() {
let a = 10
return function c(b) {
a = a - b
return a
}
}
let d = test()
d(1)
用一開始的函式做示範,他的執行堆就會如同以下的部分:
cEC:{
AO: {
b: undefined
}
scopeChain: [cEC.AO, testEC.AO, globalEC.VO]
}
testEC: {
AO: {
a: 10,
c: function
}
scopeChain: [testEC.AO, globalEC.VO]
}
globalEC: {
VO: {
d: function,
test: function
}
scopeChain: [globalEC.VO]
}
正常情況下,testEC.AO
在沒有被使用的情況下,記憶體會被釋放,但由於 function c
被 return,而且其範圍鏈中有 testEC.AO
,因此記憶體沒有被釋放,所以 a
這個變數才被保留下來,而這個就是閉包的原理。
有趣的是,以上是我們在文章上常看見對於閉包的解釋,但實際上在 JavaScropt 中,所有函式都可以被認為是閉包,主要的原因可以參考 Huli 大大所寫的 所有的函式都是閉包:談 JS 中的作用域與 Closure。
以上筆記參考 Huli 的課程 [JS201] 進階 JavaScript:那些你一直搞不懂的地方
]]>好處:在 func1 中的變數 可被 func2 使用,而 func1 的變數不會被外層讀取。
壞處:資料不會正常的被銷毀,而會一直留著。
執行的內部函式的方式會是 fun1()() 後方括弧表示呼叫內部函式,假如將函式賦予在另一個變數上,內部的變數就會存在內層,而隨著函式執行而不斷改變。
如同以下的範例:
function test() {
let a = 10
return function c(b) {
a = a - b
return a
}
}
let d = test()
d(1) // 9
d(1) // 8
d(1) // 7
那閉包的原理又是什麼呢?為什麼當中的變數會留著而記憶體不會被釋放呢?這時候就需要提到另一個東西了,叫做範圍鏈 (Scope Chain)
Execution Contexts (執行環境) 被建立時, Scope Chain、變數、 this 都被建立且初始化。
進入 function EC 的時候,範圍鏈的初始化會包含一個 Activation Object (AO) 以及 [[Scope]]
,function 的 [[Scope]]
是當被宣告時而決定的,也就是 scope chain 會是 [AO, [[Scope]] ]
。
當 function 被建立時,AO 被初始化,且會有一個 arguments 的屬性,而 AO 可以當作 VO 來使用,而 VO 就是前面所說的,在每個執行環境建立時都會有一個 Variable Object (VO),用於存放被宣告的變數及函式等。
var a = 1
function test() {
var b = 2
function test2() {
var c = 3
console.log(b)
console.log(a)
}
test2()
}
test()
用以上的程式碼作為示範,整體的執行堆會像以下所寫的部分:
test2 EC: {
AO: {
c:3
},
scopeChain: [test2EC.AO, test2.[[Scope]]]
// [test2EC.AO, test2EC.[[Scope]]] = [test2EC.AO, testEC.scopeChain] = [test2EC.AO, testEC.AO, globalEC.VO]
}
test EC: {
AO: {
b: 2,
test2: function
},
scopeChain: [testEC.AO, test.[[Scope]]]
// [testEC.AO, testEC.[[Scope]]] = [testEC.AO, globalEC.VO]
}
globalEC: {
VO: {
a: 1,
test: function
},
scopeChain: [globalEC.VO]
}
首先會初始化的是 Global EC
裡面會有一個 VO 並宣告變數 a = undefined
及 test = func
再來建立自己的 Scope Chain 也就是 GlobalEC.VO,最後賦值 a = 1
。
呼叫 test()
於是,test EC 被建立,並疊在 Global EC 上,形成執行堆,
由於是函式,因此建立的會是 AO
並宣告變數 b = undefined
及 test2 = func
建立 Scope Chain,函式的 Scope Chain 會是
testEC.AO, testEC.[[Scope]]
,
testEC.[[Scope]] = GlobalEC.scopeChain = GlobalEC.VO
,
也就是說,testEC.scopeChain = [testEC.AO, GlobalEC.VO]
,
之後賦值 b = 2
。
呼叫 test2()
依照同樣的步驟,test2EC.scopeChain
會是 test2EC.AO, [[Scope]]
等同於 test2EC.AO, testEC.scopeChain
再拆成 test2EC.AO, testEC.AO, globlaEC.scopeChain
最後變成 test2EC.AO, testEC.AO, globlaEC.VO
。
這樣就可以很清楚的看出來,test2()
執行後,log 出的資料會是 2 跟 1,用 let
或 const
也會是同樣的結果,因為範圍鏈的關係,所以內層的函式可以藉由外層的 VO/AO 而得到變數。
function test() {
let a = 10
return function c(b) {
a = a - b
return a
}
}
let d = test()
d(1)
用一開始的函式做示範,他的執行堆就會如同以下的部分:
cEC:{
AO: {
b: undefined
}
scopeChain: [cEC.AO, testEC.AO, globalEC.VO]
}
testEC: {
AO: {
a: 10,
c: function
}
scopeChain: [testEC.AO, globalEC.VO]
}
globalEC: {
VO: {
d: function,
test: function
}
scopeChain: [globalEC.VO]
}
正常情況下,testEC.AO
在沒有被使用的情況下,記憶體會被釋放,但由於 function c
被 return,而且其範圍鏈中有 testEC.AO
,因此記憶體沒有被釋放,所以 a
這個變數才被保留下來,而這個就是閉包的原理。
有趣的是,以上是我們在文章上常看見對於閉包的解釋,但實際上在 JavaScropt 中,所有函式都可以被認為是閉包,主要的原因可以參考 Huli 大大所寫的 所有的函式都是閉包:談 JS 中的作用域與 Closure。
以上筆記參考 Huli 的課程 [JS201] 進階 JavaScript:那些你一直搞不懂的地方
]]><div id="app">
{{ message }}
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello'
}
})
</script>
這樣就會在畫面上呈現出,message
所代表的 Hello
,而在 {{}} 中,我們是可以直接在上面使用 JavaScript 的方法,像是 split()
, join()
, splice()
等,來改變資料呈現的樣式,所以當我們需要的資料格式與原有的不符,我們就可以做出調整。像是透過 {{ message.split('').reverse().join('') }}
就可以在畫面上得到顛倒過來的 olleH
。但這樣一來,程式碼看起來可讀性較差,而且也比較不容易維護,這時候就會需要用到 Vue 當中的計算屬性 (computed),大部分會以資料驅動資料的方式進行。
寫法如下:
<div id="app">
{{ reversedmessage }}
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello'
},
computed: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
})
</script>
這樣一來,就可以順利在畫面呈現想要的資料了,但這樣的寫法和我們一般拿來放函式的 methods
有什麼差別呢?
以下的程式碼在畫面上會呈現相同的資料:
<div id="app">
{{ reversedmessage }}
{{ reversedmessage1() }}
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello'
},
computed: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
},
methods: {
reversedMessage1: function () {
return this.message.split('').reverse().join('')
}
}),
</script>
methods
呼叫時,由於是 function
形式,所以需要呼叫,才能呈現出資料。computed
有一個 caching
的功能,假如函式中所相依的資料被改變,computed
才會更新,而 methods
則是無論如何都會執行。computed
和 methods
都會執行,但當我們呼叫時,因為相依的資料 (message
) 沒有改變,因此 computed
中的 console.log('computed')
沒有再一次執行,會將之前處理過的資料回傳。methods
也一起動了?
改變 data
中的資料時,依照先前所說的,應該會只有 computed
的部份會跟著連動,但 methods
的部分卻也一起更新,這是為什麼呢?
當在 methods
及 computed
中個別加入一個使用到 message
資料的函式並改變資料,這時候會發現依然只有原先的兩個函式被呼叫了。也就是說,只有影響到畫面上資料呈現的部分,才會受到影響,也就是資料改變導致了畫面重新載入,所以不管是 computed
或 methods
只要和畫面上的資料有關,就會一起被連動。
所以,當我們將呈現資料的部分註解掉後,再改變資料,這樣一來,不管是 methods
或 computed
只要不在畫面上就不會執行。這裡需要注意的部分是,display: none;
和 v-show
因為 Dom 存在所以同樣是在畫面上所以會執行,但假如使用 v-if
就不會有效果。
而這是因為,Vue.js devtools
為了顯示內容,會強制執行這些函式,就算目前並沒有使用到。
下一篇則會說到 Vue 中的 watch
。
資料來源:Alex 宅幹嘛
]]><div id="app">
{{ message }}
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello'
}
})
</script>
這樣就會在畫面上呈現出,message
所代表的 Hello
,而在 {{}} 中,我們是可以直接在上面使用 JavaScript 的方法,像是 split()
, join()
, splice()
等,來改變資料呈現的樣式,所以當我們需要的資料格式與原有的不符,我們就可以做出調整。像是透過 {{ message.split('').reverse().join('') }}
就可以在畫面上得到顛倒過來的 olleH
。但這樣一來,程式碼看起來可讀性較差,而且也比較不容易維護,這時候就會需要用到 Vue 當中的計算屬性 (computed),大部分會以資料驅動資料的方式進行。
寫法如下:
<div id="app">
{{ reversedmessage }}
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello'
},
computed: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
})
</script>
這樣一來,就可以順利在畫面呈現想要的資料了,但這樣的寫法和我們一般拿來放函式的 methods
有什麼差別呢?
以下的程式碼在畫面上會呈現相同的資料:
<div id="app">
{{ reversedmessage }}
{{ reversedmessage1() }}
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello'
},
computed: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
},
methods: {
reversedMessage1: function () {
return this.message.split('').reverse().join('')
}
}),
</script>
methods
呼叫時,由於是 function
形式,所以需要呼叫,才能呈現出資料。computed
有一個 caching
的功能,假如函式中所相依的資料被改變,computed
才會更新,而 methods
則是無論如何都會執行。computed
和 methods
都會執行,但當我們呼叫時,因為相依的資料 (message
) 沒有改變,因此 computed
中的 console.log('computed')
沒有再一次執行,會將之前處理過的資料回傳。methods
也一起動了?
改變 data
中的資料時,依照先前所說的,應該會只有 computed
的部份會跟著連動,但 methods
的部分卻也一起更新,這是為什麼呢?
當在 methods
及 computed
中個別加入一個使用到 message
資料的函式並改變資料,這時候會發現依然只有原先的兩個函式被呼叫了。也就是說,只有影響到畫面上資料呈現的部分,才會受到影響,也就是資料改變導致了畫面重新載入,所以不管是 computed
或 methods
只要和畫面上的資料有關,就會一起被連動。
所以,當我們將呈現資料的部分註解掉後,再改變資料,這樣一來,不管是 methods
或 computed
只要不在畫面上就不會執行。這裡需要注意的部分是,display: none;
和 v-show
因為 Dom 存在所以同樣是在畫面上所以會執行,但假如使用 v-if
就不會有效果。
而這是因為,Vue.js devtools
為了顯示內容,會強制執行這些函式,就算目前並沒有使用到。
下一篇則會說到 Vue 中的 watch
。
資料來源:Alex 宅幹嘛
]]>function test() {
console.log(a)
var a = 2
}
test()
這時候,會輸出的是 undefined
而不是跳出錯誤訊息說 a is not defined
,其中 前者是表示 a = undefined
而後者則是表示 a
這個變數並沒有被宣告所以不存在。
但依照程式一行一行執行的觀念,此時的 a
應該是沒有被宣告的才對,那 undefined
是怎麼來的呢?而這個就是我們常聽到的提升 (hoisting) 的概念。
而在講提升之前,必須要大概了解一下,在我們執行 JS 的程式碼時,發生了什麼。這邊會在關係到其他兩個名詞,一個是 Execution Contexts (執行環境),另一個則是 Execution Stack (執行堆)
簡單來說就是一個 {} 所包圍的部分。每個執行環境中,都預設有一個 Variable Object (VO)
,而當變數或函式被宣告的時候,就會在 VO 中添加 properties,假如宣告的變數在還沒賦值之前則會給予預設值 undefined
,相同的變數則會進行覆蓋的動作。而執行程式碼時,第一個建立的環境會是 Global Execution Context (全域執行環境)。
執行堆則是一個排隊等待的地方(?,簡單來說,每當一個執行環境被建立時,就會堆疊在舊的執行環境上。當所有的執行環境都被建立完成後,JS 就會開始依序執行執行環境中的內容,執行完後則換下一個執行環境,一直到所有執行環境的內容都被執行完畢。
function a() {
b()
console.log(3)
}
function b() {
c()
console.log(2)
}
function c() {
console.log(1)
}
a()
以上面的程式碼為例,最先被創造的會是全域執行環境,再來會是被呼叫的 a()
,接著是 b()
最後是 c()
,也就是說執行堆的由上至下分別是 c()
=> b()
=> a()
=> 全域執行環境
,所以執行這段程式碼後,依序會 log 出 1 2 3。但必須要注意的是,當把 log 的程式碼放到了呼叫函式的程式碼前面,log 的程式碼會先被執行。
也就是說,在每個執行環境中,會依照順序由上往下執行程式碼,而遇到了另一個執行環境時,則會創造一個新的執行環境 (執行堆往上堆疊),執行完畢後才會回到原本的環境繼續執行剩餘的程式碼。
講這麼多,提升到底是怎麼發生的呢?其實從執行環境的初始化就可以看到一些端倪了。
假如宣告的變數在還沒賦值之前則會給予預設值
undefined
也就是當執行環境創造出來的時候,會先找有沒有變數宣告,假如有會先用 undefined
去當成變數的預設值,之後再去執行環境中的程式碼。
function test() {
console.log(a)
var a = 2
}
test()
所以再看一次這段程式碼,當 test()
這個執行環境被創造,發現有 a
這個變數被宣告,因此在還沒賦值 var a = 2
之前,a
會是預設值 undefined
。所以 log 出來的就會是預設值 undefined
。
那提升有順序可言嗎?
答案是有的,我們可以用下面函式來驗證。
function test() {
console.log(a)
function a() {}
var a = 2
}
test()
當執行這段程式碼的時候,不管前後順序,輸出的 a
都會是一個函式,也就是說函式的提升順序是高於變數的。
function test(a) {
console.log(a)
function a() {}
var a = 2
}
test(123)
接下來,假如我們在函式中帶入參數呢?執行的結果依然會是一個函式,但假如將函式那段程式碼移除,結果就會變成 123
。這樣一來我們就可以排出提升的順序是:函式 => 函式的參數 => 變數宣告,這樣的結果。
以上的結果都是在使用 var
宣告變數的情形,但在 ES6 的時候,出現了另外兩個宣告變數的方式,分別是 let
跟 const
。這時候,就產生了一些變化。
function test() {
console.log(a)
let a = 2
}
test()
相同的程式碼,只是將宣告的方法改成了 let
,執行後就會跳出錯誤訊息說 a is not defined
而方法改成 const
也會是同樣的結果,那是表示說 let
跟 const
是沒有提升嗎?
var a = 1
function test() {
console.log(a)
var a = 2
}
test()
以上這段程式碼執行後,會因為 var
的提升而輸出 undefined
,而當我們將函數內的 var a = 2
移除,就會輸出 1
let a = 1
function test() {
console.log(a)
let a = 2
}
test()
但當我們把 var
改成 let
的時候,得到的依然是 a is not defined
的錯誤訊息,所以 let
跟 const
還是有提升,就像下方的程式碼,只是表現方式和 var
不同。
let a = 1
function test() {
let a
console.log(a)
a = 2
}
test()
而這裡又必須提到一個名詞叫做,暫時死區 (Temporal Dead Zone, TDZ),這個區域會在使用 let
及 const
的時候出現,並存在於宣告及賦值之間 (let a
和 a = 2
),只要在這個區域中想要存取該變數,就會出現 is not defined
的錯誤訊息。也就是說,當我們使用 let
與 const
時,想要取用該變數,就必須在賦值之後才能取用到。
以上就是關於 hoisting 的一些資訊,大部分算是課程的筆記 (JavaScript 全攻略:克服JS 的奇怪部分、進階 JavaScript:那些你一直搞不懂的地方),有任何錯誤的地方也麻煩提醒我一下,非常感謝~
]]>function test() {
console.log(a)
var a = 2
}
test()
這時候,會輸出的是 undefined
而不是跳出錯誤訊息說 a is not defined
,其中 前者是表示 a = undefined
而後者則是表示 a
這個變數並沒有被宣告所以不存在。
但依照程式一行一行執行的觀念,此時的 a
應該是沒有被宣告的才對,那 undefined
是怎麼來的呢?而這個就是我們常聽到的提升 (hoisting) 的概念。
而在講提升之前,必須要大概了解一下,在我們執行 JS 的程式碼時,發生了什麼。這邊會在關係到其他兩個名詞,一個是 Execution Contexts (執行環境),另一個則是 Execution Stack (執行堆)
簡單來說就是一個 {} 所包圍的部分。每個執行環境中,都預設有一個 Variable Object (VO)
,而當變數或函式被宣告的時候,就會在 VO 中添加 properties,假如宣告的變數在還沒賦值之前則會給予預設值 undefined
,相同的變數則會進行覆蓋的動作。而執行程式碼時,第一個建立的環境會是 Global Execution Context (全域執行環境)。
執行堆則是一個排隊等待的地方(?,簡單來說,每當一個執行環境被建立時,就會堆疊在舊的執行環境上。當所有的執行環境都被建立完成後,JS 就會開始依序執行執行環境中的內容,執行完後則換下一個執行環境,一直到所有執行環境的內容都被執行完畢。
function a() {
b()
console.log(3)
}
function b() {
c()
console.log(2)
}
function c() {
console.log(1)
}
a()
以上面的程式碼為例,最先被創造的會是全域執行環境,再來會是被呼叫的 a()
,接著是 b()
最後是 c()
,也就是說執行堆的由上至下分別是 c()
=> b()
=> a()
=> 全域執行環境
,所以執行這段程式碼後,依序會 log 出 1 2 3。但必須要注意的是,當把 log 的程式碼放到了呼叫函式的程式碼前面,log 的程式碼會先被執行。
也就是說,在每個執行環境中,會依照順序由上往下執行程式碼,而遇到了另一個執行環境時,則會創造一個新的執行環境 (執行堆往上堆疊),執行完畢後才會回到原本的環境繼續執行剩餘的程式碼。
講這麼多,提升到底是怎麼發生的呢?其實從執行環境的初始化就可以看到一些端倪了。
假如宣告的變數在還沒賦值之前則會給予預設值
undefined
也就是當執行環境創造出來的時候,會先找有沒有變數宣告,假如有會先用 undefined
去當成變數的預設值,之後再去執行環境中的程式碼。
function test() {
console.log(a)
var a = 2
}
test()
所以再看一次這段程式碼,當 test()
這個執行環境被創造,發現有 a
這個變數被宣告,因此在還沒賦值 var a = 2
之前,a
會是預設值 undefined
。所以 log 出來的就會是預設值 undefined
。
那提升有順序可言嗎?
答案是有的,我們可以用下面函式來驗證。
function test() {
console.log(a)
function a() {}
var a = 2
}
test()
當執行這段程式碼的時候,不管前後順序,輸出的 a
都會是一個函式,也就是說函式的提升順序是高於變數的。
function test(a) {
console.log(a)
function a() {}
var a = 2
}
test(123)
接下來,假如我們在函式中帶入參數呢?執行的結果依然會是一個函式,但假如將函式那段程式碼移除,結果就會變成 123
。這樣一來我們就可以排出提升的順序是:函式 => 函式的參數 => 變數宣告,這樣的結果。
以上的結果都是在使用 var
宣告變數的情形,但在 ES6 的時候,出現了另外兩個宣告變數的方式,分別是 let
跟 const
。這時候,就產生了一些變化。
function test() {
console.log(a)
let a = 2
}
test()
相同的程式碼,只是將宣告的方法改成了 let
,執行後就會跳出錯誤訊息說 a is not defined
而方法改成 const
也會是同樣的結果,那是表示說 let
跟 const
是沒有提升嗎?
var a = 1
function test() {
console.log(a)
var a = 2
}
test()
以上這段程式碼執行後,會因為 var
的提升而輸出 undefined
,而當我們將函數內的 var a = 2
移除,就會輸出 1
let a = 1
function test() {
console.log(a)
let a = 2
}
test()
但當我們把 var
改成 let
的時候,得到的依然是 a is not defined
的錯誤訊息,所以 let
跟 const
還是有提升,就像下方的程式碼,只是表現方式和 var
不同。
let a = 1
function test() {
let a
console.log(a)
a = 2
}
test()
而這裡又必須提到一個名詞叫做,暫時死區 (Temporal Dead Zone, TDZ),這個區域會在使用 let
及 const
的時候出現,並存在於宣告及賦值之間 (let a
和 a = 2
),只要在這個區域中想要存取該變數,就會出現 is not defined
的錯誤訊息。也就是說,當我們使用 let
與 const
時,想要取用該變數,就必須在賦值之後才能取用到。
以上就是關於 hoisting 的一些資訊,大部分算是課程的筆記 (JavaScript 全攻略:克服JS 的奇怪部分、進階 JavaScript:那些你一直搞不懂的地方),有任何錯誤的地方也麻煩提醒我一下,非常感謝~
]]>