作用域:
變量所在的上下文,指的是變量在哪些地方可以訪問
對于JavaScript來說有全局作用域但是沒有塊級作用域,在ES6中引入了關(guān)鍵字let可以生成塊作用域.見以下代碼:
var value = true
if (value) {
var age = 18
console.log(`我今年${age}歲了`)
}
console.log (`我也是${age}歲了哦`)
//輸出 "我今年18歲了" "我也是18歲了哦"
// 可見在if()中不存在塊級作用域
在這里if()里的變量具有全局作用域,全局皆可使用
var value = true
if (value) {
let age = 18
console.log(`我今年${age}歲了`)
}
console.log (`我也是${age}歲了哦`)
/*輸出
"我今年18歲了"
"error"
"ReferenceError: age is not defined*/
在這里使用關(guān)鍵字let 使 if () 塊里的變量age產(chǎn)生了塊級作用域,使得它只在這個塊里生效.
JS中有函數(shù)作用域,指的是作用域在函數(shù)內(nèi)部。這里一共說了三種作用域,其實可以說是兩種:一種是全局作用域琉朽,而是局部作用域(函數(shù)作用域莲趣、塊級作用域)惋砂,塊級作用域概念又包括了函數(shù)作用域呆躲。
簡要說下幾個作用域聲明方式:
- 全局作用域:在所有函數(shù)外部使用var語句聲明變量或者在聲明變量時忽略var則會隱式轉(zhuǎn)化為全局變量
- 函數(shù)作用域: 需要在函數(shù)內(nèi)部使用var 聲明變量才行
- 塊級作用域: 在變量名前添加let語句聲明(ES6)
作用域鏈
var a = "你好绽乔,我是a";
function scopeChain(a) {
var b =1;
function inScope(a) {
var c = "螞蟻"
console.log(`大象愛${c}`)
console.log(`我是最內(nèi)層的函數(shù)补疑,這里也可以使用a: ${a}`)
}
console.log(`能使用a嗎歧沪?${a}`)
inScope(a)
}
scopeChain(a)
這里a是全局作用域下的變量,b是函數(shù)scopeChain()作用域下的變量莲组,而c是函數(shù)scopeChain()里的inScope()函數(shù)作用域下的變量诊胞。
作用域鏈的前端始終是當前環(huán)境作用域下變量對象,逐層往外作用域鏈接锹杈,最后端是全局變量環(huán)境下的變量撵孤,這些變量時鏈接在一起,在解析一個變量時從鏈前端往后端搜索(從內(nèi)不找外部找)竭望,但是有一點值得注意:每個變量的作用域總是從自身聲明的作用域往外找邪码,而不是調(diào)用它的地方 :
var a = 1
function fn1(){
function fn3(){
var a = 4
fn2()
}
var a = 2
return fn3
}
function fn2(){
console.log(a)
}
var fn = fn1()
fn()
//輸出1
這里就是當fn1() 執(zhí)行后調(diào)用 fn2() 時,發(fā)現(xiàn)fn3()作用域下沒有咬清,也就是作用域鏈前端沒有闭专,往外找一層也就是fn1()作用域下進行查找,作用域也就往后端前進了一步旧烧,發(fā)現(xiàn)還是沒有影钉,繼續(xù)往外層作用域查找找到了全局作用域,也就是作用域鏈的最后端掘剪,找到了后調(diào)用它平委。
但是對于fn2()來說它需要調(diào)用a這個變量,這里也就出現(xiàn)了誤區(qū):在fn3()里有變量a夺谁,那么是用的是這個變量a嗎廉赔?
- 但其實fn2()是發(fā)現(xiàn)不了這個變量a的肉微,因為fn2()聲明的地方并不在fn3()里,同理fn1()也不是fn2()聲明的地方蜡塌,所以對于fn2()來說它只發(fā)現(xiàn)了全局下的
var a =1
所以調(diào)用它并輸出a時也就等于1.
var a = 1
function fn1(){
function fn2(){
console.log(a)
}
function fn3(){
var a = 4
fn2()
}
var a = 2
return fn3
}
var fn = fn1()
fn()
//輸出多少碉纳?
- 由以上的論述可以分析出這段代碼,當fn1()被調(diào)用時也就是
return fn3
,也就調(diào)用了fn3()岗照,然后fn3()里又是調(diào)用fn2()村象,而fn() 是在fn1()里聲明的,自然也就使用了fn1()里的變量a攒至,又因為a變量在調(diào)用前聲明并賦值了,故此輸出為2
var a = 1
function fn1(){
function fn3(){
function fn2(){
console.log(a)
}
var a
fn2()
a = 4
}
var a = 2
return fn3
}
var fn = fn1()
fn()
//輸出多少躁劣?
分析這段代碼迫吐,發(fā)現(xiàn)與上面代碼不同之處在于先聲明了 ``` var a ``后沒有里脊賦值,在調(diào)用了fn2()后再進行的賦值账忘,那么這里應(yīng)該是多少呢志膀?
- 這里也就牽涉到了聲明前置,對于fn3()下鳖擒,當聲明
var a
時溉浙,也就是執(zhí)行到了fn3()代碼前,函數(shù)聲明和變量聲明會提前至代碼前端蒋荚,所以這里聲明并沒有影響到輸出值得改變戳稽,但是賦值操作是按照程序順序執(zhí)行的,當調(diào)用前期升,a只聲明沒有賦值惊奇,則會輸出undefined。 而具體變量查找是符合作用域鏈的順序來的.
總結(jié)如下:
- 函數(shù)在執(zhí)行的過程中播赁,先從自己內(nèi)部找變量
- 如果找不到颂郎,再從創(chuàng)建當前函數(shù)所在的作用域去找, 以此往上
- 注意找的是變量的當前的狀態(tài)