什麼是作用域
來一段《你不知道的JavaScript-上卷》中的原話:
險些一切編程言語最基本的功用之一,就是能夠貯存變量當中的值,而且能在以後對這個 值舉行接見或修正,這些變量住在那裡?換句話說,它們貯存在那裡?最重要的是,順序須要時怎樣找到它們?這些題目申明須要一套設想優越的劃定規矩來存儲變量,而且以後能夠方便地找到這些變量。這套劃定規矩被稱為作用域
總之就是作用域就是一套劃定規矩,這個劃定規矩劃定了順序怎樣去找到變量
詞法作用域
先看一個例子:
function func1() {
console.log(index)
}
function func2() {
var index = 10
func1()
}
var index = 100
func2() // 100
為啥是100而不是10呢???
由於JavaScript是詞法作用域
詞法作用域簡樸地說就是:函數的作用域在聲明的時刻就決議好了。和詞法作用域相對的是動態作用域,動態作用域關注函數從那邊挪用
上面的代碼中,聲明func1時,它就處於全局作用域中,所以index就是100,縱然實行func1時也是。
函數作用域和塊作用域
前面講了JavaScript是詞法作用域,那末什麼時刻會建立作用域呢???
JavaScript重要是基於函數級別的作用域,也就是每個函數都邑建立一個作用域。為何說重要呢?由於with和try-catch語句也完成了塊作用域,固然了用的很少。
var flag = true;
if (flag) {
var num = 10
}
console.log(num) // 10
如果有塊級作用域的話,此時打印num應當報錯
function funcLevelScope() {
var index = 10
}
console.log(index); // ReferenceError
由於是函數級別的作用域,所以在函數表面接見不到函數內部的變量
怎樣模仿塊作用域
固然了,第一迴響反映就是在代碼錶面加上包裝函數不就行了,比方這模樣:
function scope() {
var flag = true;
if (flag) {
var num = 10
}
}
console.log(num) // ReferenceError
然則此時的題目就是多了一個函數包裝,且我們能夠隨便的去運轉scope函數,這可能會形成不好的影響。
自實行函數處置懲罰了這個題目:
(function scope() {
var flag = true;
if (flag) {
var num = 10
}
})()
scope() // ReferenceError
console.log(num) // ReferenceError
包裝函數的聲明以 (function… 而不僅是以 function… 最先。只管看上去這並不 是一個很顯眼的細節,但實際上倒是非常重要的區分。函數會被看成函數表達式而不是一個規範的函數聲明來處置懲罰。
作用域內聲明提拔
變量聲明提拔
起首明白的是
var x = 10
像如許一句代碼能夠分為聲明和賦值兩句:
var x
x = 10
明白這個觀點再繼承進修
console.log(x) // undefined
var x = 10
為何不直接報錯呢?
由於上面的代碼片斷即是:
var x // 聲明提早
console.log(x)
x = 10
注意到變量的聲明提早到了當前作用域的最前面
函數聲明提拔
hoist() // hoist!
function hoist() {
console.log("hoist!")
}
為啥函數會勝利實行了,由於上面的代碼片斷相當於:
function hoist() {
console.log("hoist!")
}
hoist()
函數聲明被提拔到了作用域的最前面
那末函數表達式會被提拔嗎??
hoist() // TypeError
var hoist = function () {
console.log("hoist")
}
由於這個相當於:
var hoist
hoist() // TypeError
hoist = function () {
console.log("hoist")
}
變量聲明提拔和函數聲明提拔的優先級
先說結論:函數會起首被提拔,然後才是變量
看例子:
console.log(x)
var x = 10
function x() {
console.log('func x')
}
明顯此時打印的x是一個函數
這是由於上面的代碼片斷相當於:
function x() {
console.log('func x')
}
var x
console.log(x) // f x(){...}
x = 10
所以打印的是函數