今天要來了解一下一個很重要小細節概念就是Scope,了解Scope運作的概念,才能知道你的變數使用的規則,不然就會發生,我變數宣告了,為什麼不能用的情況
內容目錄
什麼是Scope
Scope又被稱為作用域/範疇…等,是JavaScript在查找變數的一套規則。就是
變數可以被取用,被看到的有效範圍
在ES6以前只有分為Global Scope和Function Scope
在ES6之後就新增了let跟const的宣告方式,也就增加了Block Scope
而Function Scope跟Block Scope可以合稱為 Local Scope
Global Scope
全域作用域,這是指不在Function Scope或Block Scope裡面的變數,只要在Global Scope宣告,任何地方都可以使用這個的變數
EX:
我們宣告globalText的變數,就是在Global Scope環境宣告的,在fun的funcion內跟外執行顯示globalText
var globalText = 'globalText'
function fun() {
console.log('在function裡面', globalText) //在function裡面 globalText
}
fun()
console.log('在function外面', globalText) //在function外面 globalText
得到的結果就是兩個都能執行成功

Function Scope
函式作用域為Local Scope的其中之一,這顧名思義就是在function(函式)內才可以使用的”變數”跟”function(函式)”,只要function之外就不可以使用
EX:
我們先來看一段正常執行的程式
在fun()這個function中宣告了funText的變數,也在fun() 裡面執行了funText;然後宣告了sayHello() 的function在fun()裡面,同時也在fun()裡面執行了sayHello()
function fun() {
var funText = 'funText'
console.log('在function裡面', funText) //在function裡面 funText
function sayHello() {
console.log('在fun裡面 說你好') //在fun裡面 說你好
}
sayHello()
}
fun()
得到的結果也是兩邊的執行成功

那差在哪裡呢,就是差在如果把function裏面的變數或是函式拿到外面來,就會顯示錯誤
EX:
在fun()這個function中宣告了funText的變數,但在”fun() 外面”執行了funText;然後宣告了sayHello() 的function在fun()裡面,但也在”fun()外面”執行了sayHello()
function fun() {
var funText = 'funText'
function sayHello() {
console.log('在fun裡面 說你好')
}
}
fun()
console.log('在function裡面', funText) //Uncaught ReferenceError: funText is not defined
sayHello() //Uncaught ReferenceError: sayHello is not defined
得到的結果就是錯誤訊息


Block Scope
區塊作用域為Local Scope的其中之一,在ES6之前,就只有function可以建立Scope,在ES6後就可以在大括號內建立Scope,而同樣的在大括號外就不可以使用, if 判斷式 、while 或是 for 迴圈語法用到的大括號範圍就是 Block
而要使用這個方式,就要使用文章開頭提到的let、const的宣告
先來看成功執行的部分
分別宣告varText、letText、constTexty在大括號內,也同樣的在括號內執行三個變數
{
var varText = "varText"
let letText = "letText"
const constText = "constText"
console.log('在括號內執行', varText) //在括號內執行 varText
console.log('在括號內執行', letText) //在括號內執行 letText
console.log('在括號內執行', constText) //在括號內執行 constText
}
執行結果是成功執行

那如果將所有的變數移到大括號外使用呢
分別宣告varText、letText、constTexty在大括號內,並且將”在括號外”執行三個變數
{
var varText = "varText"
let letText = "letText"
const constText = "constText"
}
console.log('在括號內執行', varText) //在括號內執行 varText
console.log('在括號內執行', letText) //Uncaught ReferenceError: letText is not defined
console.log('在括號內執行', constText) //Uncaught ReferenceError: constText is not defined


得到的結果是只有var宣告的變數,不會被限制在括號內,而如果要使用block Scope的話,就必須要用let跟const去宣告
宣告 Local Scope的好處
- 最小權限原則
- 避免衝突
最小權限原則
避免被不當存取,他存的變數是其他碼不需要知道的資料,所以對於其他地方是無法直接存取或使用的,只能透過公開處理過的方式取得,這樣可以避免被人串改或是不當利用
避免衝突
假設有一個變數或函式在不同區域都要被利用到,但是兩個存取的資料是不同的,在這狀態下則可以避免衝突
EX先示範一個發生衝突的
在Global Scope(全域作用域)中,我們設定一個sumNum,並且設定一個sumNum加10次的函式plusNum(),跟sumNum減10次的函式reduceNum()
let sumNum = 0
function plusNum() {
for (i = 0; i < 10; i++) {
sumNum++
}
console.log('plusNum函式呼叫結果', sumNum) //plusNum函式呼叫結果 10
}
function reduceNum() {
for (i = 0; i < 10; i++) {
sumNum--
}
console.log('reduceNum函式呼叫結果', sumNum) //reduceNum函式呼叫結果 0
}
plusNum()
reduceNum()
得到的結果是,因為sumNum在Global Scope宣告,所以一開始被加10次得到是10,但在減的時候被呼叫過去從原本的10開始減10次變成0

再以同樣例子我們去把他都丟到自己的函式宣告
設定一個宣告sumNum,並且sumNum加10次的函式plusNum(),跟宣告sumNum,並且sumNum減10次的函式reduceNum()
function plusNum() {
let sumNum = 0
for (i = 0; i < 10; i++) {
sumNum++
}
console.log('plusNum函式呼叫結果', sumNum) //plusNum函式呼叫結果 10
}
function reduceNum() {
let sumNum = 0
for (i = 0; i < 10; i++) {
sumNum--
}
console.log('reduceNum函式呼叫結果', sumNum) //reduceNum函式呼叫結果 -10
}
plusNum()
reduceNum()
得到的結果是plusNum()結束後一樣獲得加10次的結果,reduceNum()則是變成-10的結果,兩邊的結果不會互相衝突

結論
這在寫程式中會有很多小細節的錯誤,有時候會摸不清頭緒,明明邏輯正確,得到的資料卻天差地遠,可能就是沒搞清楚變數在不同Scope的規範是什麼