學(xué)廢了鸟整,JavaScript 中的作用域與作用域鏈

什么是作用域?

作用域定義了變量的可見(jiàn)性或可訪問(wèn)性川慌。大白話來(lái)說(shuō)吃嘿,就是一個(gè)變量能不能被訪問(wèn)或引用,是由它的作用域決定的梦重。

在 JavaScript 中有三種作用域兑燥。

  • 全局作用域
  • 函數(shù)作用域(局部作用域)
  • 塊作用域
let globalVariable = "我是全局作用域下的變量"
function func() {
    let localVariable = "我是局部作用域下的變量"
}

if (true) {
    let blockVariable = "我是塊作用域下的變量"
}

全局作用域 Global Scope

一個(gè)在最外層定義的變量便處于全局作用域,全局作用域內(nèi)的變量可以在程序的任意地方訪問(wèn)琴拧。

var globalVariable = "全局作用域變量"
function func() {
    // 在函數(shù)內(nèi)訪問(wèn)全局作用域的變量
    console.log("函數(shù)內(nèi)訪問(wèn):", globalVariable)
}

func()
console.log("函數(shù)外訪問(wèn):", globalVariable)

輸出:

函數(shù)內(nèi)訪問(wèn): 全局作用域變量
函數(shù)外訪問(wèn): 全局作用域變量

使用 var 關(guān)鍵字 在大括號(hào)內(nèi)(包括純粹的大括號(hào)降瞳、if、while蚓胸、for)定義的變量仍然`屬于全局作用域挣饥。

if (true) {
    var globalVariable = "全局作用域變量"
}
console.log("外部訪問(wèn):", globalVariable)

輸出:

外部訪問(wèn): 全局作用域變量

函數(shù)作用域(局部作用域) Function Scope(Local Scope)

在函數(shù)內(nèi)定義的變量則屬于函數(shù)作用域,又稱局部作用域沛膳。局部作用域內(nèi)的變量只能在自身作用域內(nèi)被訪問(wèn)扔枫。

function func(params) {
    var localVariable = "局部作用域變量"
    console.log("函數(shù)內(nèi)訪問(wèn):", localVariable)
}

func()
console.log("外部訪問(wèn):", localVariable) // Uncaught ReferenceError: localVariable is not defined

輸出:

函數(shù)內(nèi)訪問(wèn): 局部作用域變量
Uncaught ReferenceError: localVariable is not defined

例子中,我們嘗試在外部訪問(wèn)局部作用域中定義的變量锹安,報(bào)了 變量未定義 的錯(cuò)誤短荐。

塊作用域 Block Scope

ES6 中引入了 let 與 const,與 var 不同的是叹哭。之前的例子中忍宋,在大括號(hào)(包括純粹的大括號(hào)、if风罩、while糠排、for)間用 var 定義的變量處在全局作用域。如果我們用 let 與 const 在大括號(hào)中定義超升,變量將處于塊作用域入宦。

塊作用域內(nèi)的變量只能在自身作用域內(nèi)被訪問(wèn)哺徊。

let 與 const 的不同點(diǎn)在于, const 定義的是一個(gè)常量云石,無(wú)法修改定義后的值唉工。

{
    let blockVariable = "塊作用域變量"
    console.log("塊內(nèi)訪問(wèn):", blockVariable)
}
console.log("外部訪問(wèn):", blockVariable) // Uncaught ReferenceError: blockVariable is not defined

輸出:

塊內(nèi)訪問(wèn): 塊作用域變量
Uncaught ReferenceError: blockVariable is not defined

例子中,我們嘗試在外部訪問(wèn)塊作用域中定義的變量汹忠,報(bào)了 變量未定義 的錯(cuò)誤淋硝。

什么是作用域鏈? Scope Chain

當(dāng)一個(gè)變量在當(dāng)前作用域無(wú)法找到時(shí)宽菜,便會(huì)嘗試尋找其外層的作用域谣膳,如果還找不到,再繼續(xù)往外尋找(只會(huì)往外尋找铅乡,不會(huì)尋找兄弟作用域继谚,更不會(huì)往內(nèi)尋找)。這種如同鏈條一樣的尋找規(guī)則便被稱為作用域鏈阵幸。

let variable1 = "我是變量 1花履,外部的"
let variable2 = "我是變量 2"
function func() {
    let variable1 = "我是變量 1,內(nèi)部的"
    {
        let variable3 = "我是變量 3"
    }
    {
        // 往外尋找挚赊,在上一層函數(shù)內(nèi)找到了
        console.log(variable1)
        // 往外尋找诡壁,直到全局作用域
        console.log(variable2)
        // 找不到,報(bào)錯(cuò)
        console.log(variable3) // Uncaught ReferenceError: variable3 is not defined
    }
}
func()

輸出:

我是變量 1荠割,內(nèi)部的
我是變量 2
Uncaught ReferenceError: variable3 is not defined

在例子中妹卿,打印 variable1 變量時(shí),由于在上層作用域也就是函數(shù)中就找到了 variable1 變量蔑鹦,便停止了尋找夺克,不會(huì)找到全局作用域下的 variable1 變量。

尋找 variable2 變量時(shí)嚎朽,在上層作用域中未找到铺纽,便一直找到了上上層作用域,也就是全局作用域下的 variable2 變量哟忍。

尋找 variable3 變量時(shí)室囊,由于 variable3 變量被定義在兄弟作用域中,并不會(huì)被尋找到魁索,因?yàn)樽饔糜蜴湹囊?guī)則是只會(huì)往上層作用域?qū)ふ遥⒉粫?huì)尋找兄弟作用域盼铁。因此這里報(bào)了變量未定義的錯(cuò)誤粗蔚。

函數(shù)的作用域是它定義時(shí)的作用域,而不是調(diào)用時(shí)

function func() {
    let variable = "我是 func 內(nèi)的變量"
    function func2() {
        console.log(variable)
    }
    return func2
}

{
    let variable = "我是大括號(hào)內(nèi)的變量"
    let func2 = func()
    func2()
}

輸出:

我是 func 內(nèi)的變量

在例子中饶火,執(zhí)行 func2 函數(shù)時(shí)往上尋找的作用域是在 func2 定義時(shí)的作用域鹏控,而不是調(diào)用時(shí)的作用域致扯。

如果找不到變量會(huì)怎樣?

如果一個(gè)變量直到全局作用域也找不到便會(huì)執(zhí)行以下操作当辐。

  1. 非嚴(yán)格模式:隱式聲明全局變量
  2. 嚴(yán)格模式:報(bào)錯(cuò)

非嚴(yán)格模式

非嚴(yán)格模式下抖僵,嘗試賦值一個(gè)變量時(shí),如果找不到則會(huì)隱性聲明成全局域的變量缘揪。

{
    variable = "我是一個(gè)隱性聲明的變量"
}
console.log(variable)

輸出:

我是一個(gè)隱性聲明的變量

以上的例子中耍群,variable 由于未聲明,因此被隱性聲明成了全局作用域下的變量找筝,這使得在最外部也能打印出 variable 變量的值蹈垢。

非嚴(yán)格模式下,嘗試使用一個(gè)變量的值時(shí)袖裕,如果找不到同樣會(huì)報(bào)錯(cuò)曹抬。

{
    console.log(variable) // Uncaught ReferenceError: variable is not defined
}

輸出:

Uncaught ReferenceError: variable is not defined

以上的例子中,由于使用 variable 時(shí)未定義急鳄,因此報(bào)了未定義的錯(cuò)誤谤民。

嚴(yán)格模式

加入 "use strict" 表明是嚴(yán)格模式,嚴(yán)格模式下不論賦值還是使用未事先聲明的變量都會(huì)報(bào)錯(cuò)疾宏。

"use strict"
{
    variable = "我是一個(gè)隱性聲明的變量" // Uncaught ReferenceError: variable is not defined
}

輸出:

Uncaught ReferenceError: variable is not defined

作用域的好處张足?

  1. 防止命名沖突:你寫(xiě)了一萬(wàn)行的代碼文件,如果沒(méi)有作用域灾锯,你要給每個(gè)變量取獨(dú)一無(wú)二的名字兢榨,屁股想想也知道是種折磨。
  2. 安全性: 變量不會(huì)被外部訪問(wèn)顺饮,保證了變量值不會(huì)被隨意修改吵聪。你定義在函數(shù)內(nèi)的變量,如果能在幾千行之后不小心被修改兼雄,腳趾頭想想也知道是種折磨吟逝。
  3. 更高級(jí)的語(yǔ)法:封裝、面向?qū)ο蟮鹊膶?shí)現(xiàn)離不開(kāi)對(duì)變量的隔離赦肋,這是依靠作用域所達(dá)到的块攒。

說(shuō)人話!

寫(xiě)代碼時(shí)不用區(qū)分它什么全局使用域佃乘、局部作用域囱井、塊作用域啥的概念。只用記得大括號(hào)就是一個(gè)作用域趣避,尋找變量永遠(yuǎn)是從內(nèi)往外找∨优唬現(xiàn)在我們的編輯器基本都有縮進(jìn)格式化, 從當(dāng)前代碼塊的位置一層一層往左,就是它所能引用到的所有變量住练。

打個(gè)比方地啰,就像我們每個(gè)家庭就是一個(gè)作用域,當(dāng)我們需要一筆手術(shù)費(fèi)掏不出錢的時(shí)候讲逛,肯定是先在家里找亏吝,問(wèn)問(wèn)父母兄弟姐妹啥的,不會(huì)去求助其他陌生的家庭盏混。還沒(méi)有的話就往外到熟人關(guān)系這個(gè)作用域里問(wèn)問(wèn)蔚鸥。還不行就向街道居委會(huì)求助。居委會(huì)也沒(méi)辦法再向國(guó)家求援括饶。從最親近的關(guān)系找起株茶,一層一層圈子往外,這就是作用域與作用域鏈图焰。

最后強(qiáng)烈建議大家使用 let 命名變量启盛,放棄 var!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末技羔,一起剝皮案震驚了整個(gè)濱河市僵闯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌藤滥,老刑警劉巖鳖粟,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拙绊,居然都是意外死亡向图,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門标沪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)榄攀,“玉大人,你說(shuō)我怎么就攤上這事金句¢萦” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵违寞,是天一觀的道長(zhǎng)贞瞒。 經(jīng)常有香客問(wèn)我,道長(zhǎng)趁曼,這世上最難降的妖魔是什么军浆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮挡闰,結(jié)果婚禮上乒融,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好簇抵,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著射众,像睡著了一般碟摆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上叨橱,一...
    開(kāi)封第一講書(shū)人閱讀 50,021評(píng)論 1 291
  • 那天典蜕,我揣著相機(jī)與錄音,去河邊找鬼罗洗。 笑死愉舔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伙菜。 我是一名探鬼主播轩缤,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼贩绕!你這毒婦竟也來(lái)了火的?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤淑倾,失蹤者是張志新(化名)和其女友劉穎馏鹤,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體娇哆,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡湃累,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碍讨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片治力。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖垄开,靈堂內(nèi)的尸體忽然破棺而出琴许,到底是詐尸還是另有隱情,我是刑警寧澤溉躲,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布榜田,位于F島的核電站,受9級(jí)特大地震影響锻梳,放射性物質(zhì)發(fā)生泄漏箭券。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一疑枯、第九天 我趴在偏房一處隱蔽的房頂上張望辩块。 院中可真熱鬧,春花似錦、人聲如沸废亭。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)豆村。三九已至液兽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掌动,已是汗流浹背四啰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留粗恢,地道東北人柑晒。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像眷射,于是被迫代替她去往敵國(guó)和親匙赞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容