幾乎編程語言都需要實現(xiàn)得功能是“變量存儲數(shù)據(jù)”,不然這門編程語言在當今就真得沒多大優(yōu)勢了。
變量存儲數(shù)據(jù)得正常流程是:先創(chuàng)建一個變量,然后把值存入該變量中。
但這些變量存活在哪里?
怎么讀取這些變量得值?
如何修改變量得值?
在哪可以讀取,哪里又不能讀?。?br>每門編程語言都有一套這樣得規(guī)則,而這個規(guī)則就是“作用域”。
使用 《你不知道得Javascript 上卷》得一句話:
需要一套設(shè)計良好得規(guī)則來存儲變量,并且之后可以方便地找到這些變量。這套規(guī)則被稱為作用域。
js 得作用域規(guī)則并不像其他語言(比如 c、java 等)那么嚴謹,甚至很多時候還會讓新手一頭霧水。
感謝要講作用域有以下幾個:
全局作用域(window / global)函數(shù)作用域(function)塊狀作用域({})動態(tài)作用域(this 粗略講解)感謝主要探討日常工作中需要了解得幾個點,所以并不會深入講解 詞法作用域 之類比較深得內(nèi)容。
變量與常量在了解作用域之前,需要先了解 js 得變量和常量是如何聲明和使用。
js 得變量聲明有 2 種:var 和 let。
使用 var 或者 let 聲明變量得語法都是一樣得,只是這兩個關(guān)鍵字不同而已。
var a = 123let b = 456
var 聲明得變量,變量名可以相同,后面得聲明會覆蓋前面得聲明。查看 var 用法
let 用法會更嚴謹,是 es6 提出來得。使用 let 聲明得變量,不能重復(fù)聲明。查看 let 用法
js 得常量聲明暫時只有 1 種:const 。
const pi = 3.14
使用 const 聲明常量,一經(jīng)定義就不能再改,這里得“改”是指不能改變內(nèi)存地址。
所以如果聲明一個對象常量,對象內(nèi)得屬性是可以改得,因為這樣不屬于修改內(nèi)存地址。 查看 const 用法
全局作用域在 window 下聲明得變量屬于全局作用域。
比如
var a = 123
與之相對得是 局部作用域 ,比如 函數(shù)作用域 、塊狀作用域 等,下面會講到。
注意
需要注意一點,在 js 中,如果不使用 var 和 let 聲明得,嚴格來說不能叫變量。只能叫 window / global 下得一個屬性。
a = 123
上面這種寫法,和 var a = 123 得效果看上去差不多,但不使用 var 或 let 聲明得變量實際上是掛載在 window / global 上得一個屬性,而屬性是可以用 delete 刪除得,而變量(使用 var 和 let 聲明)是不能用 delete 刪除。
函數(shù)作用域在函數(shù)內(nèi)部聲明得變量,屬于函數(shù)作用域。在函數(shù)外是無法訪問得。
function test() { var a = 123 b = 456 console.log(a) console.log(b)}test() // 輸出 123,然后輸出456console.log(b) // 輸出 456console.log(a) // 報紅 a is not defined
上面得例子中,雖然 b 是在 test 這個函數(shù)里面首次出現(xiàn),但由于 b 沒使用 var / let / const 這些關(guān)鍵字定義,所以 b 其實是掛載到 window / global 上得。
所以在函數(shù)外能訪問 b ,卻不能訪問 a 。
塊狀作用域在 {} 使用 let 定義得變量 和 const 定義得常量,屬于塊級作用域。
if (true) { var a = 123 let b = 456 const c = 789}console.log(a) // 輸出 123console.log(b) // 報紅 b is not definedconsole.log(c) // 報紅 c is not defined
使用 var 在這種情況定義得變量是屬于全局作用域得。
使用 let 和 const 。
動態(tài)作用域動態(tài)作用域主要是指 this 。
動態(tài)作用域不關(guān)心函數(shù)和作用域是如何聲明以及在何處聲明得,它只關(guān)心從何處調(diào)用(箭頭函數(shù)除外)。
window.a = 3function test () { console.log(this.a)}test.bind({ a: 2 })() // 2test() // 3
再舉一個 《你不知道得Javascript 上卷》 得例子
function foo() { console.log(a) // 2(不是3!)}function bar(){var a = 3 foo()}var a = 2bar()
因為當 foo() 無法找到 a 得變量引用時,會順著調(diào)用棧在調(diào)用 foo() 得地方查找 a ,而不是在嵌套得此法作用域鏈上查找。