作用域
作用域(scope)規(guī)定了變量能夠被訪問的“范圍”,離開了這個(gè)“范圍”變量便不能被訪問。
作用域分為:
- 局部作用域
- 全局作用域
局部作用域
局部作用域分為函數(shù)作用域和塊作用域。
函數(shù)作用域
在函數(shù)內(nèi)部聲明的變量只能在函數(shù)內(nèi)被訪問,外部無(wú)法直接訪問。
function foo(){
const bar = 1;
}
console.log(bar); // ReferenceError: bar is not defined
總結(jié):
- 函數(shù)內(nèi)部聲明的變量,在函數(shù)外部無(wú)法被訪問;
- 函數(shù)的參數(shù)也是函數(shù)內(nèi)部的局部變量;
- 不同函數(shù)內(nèi)部聲明的變量無(wú)法互相訪問;
- 函數(shù)執(zhí)行完畢后,函數(shù)內(nèi)部的變量實(shí)際被清空了。
塊作用域
在JavaScript中使用{}
包裹的代碼稱為代碼塊,代碼塊內(nèi)部聲明的變量外部將有可能無(wú)法被訪問。
有可能:取決于使用let
還是var
。
for (let i=1; i<=5; i++){
// i 只能在該代碼塊中被訪問
console.log(i); // 正常
}
// 超出了 i 的作用域
console.log(i); // 報(bào)錯(cuò)
for (var i=1; i<=5; i++){
// i 能在該代碼塊中被訪問
console.log(i); // 正常
}
// 超出了 i 的作用域
console.log(i); // 不會(huì)報(bào)錯(cuò),因?yàn)?i 是使用var聲明的
總結(jié):
- let聲明的變量會(huì)產(chǎn)生塊作用域,var不會(huì)產(chǎn)生塊作用域;
- const聲明的常量也會(huì)產(chǎn)生塊作用域;
- 不同代碼塊之間的變量無(wú)法互相訪問;
- 推薦使用let或const。
全局作用域
<script>
標(biāo)簽和.js
文件的最外層就是所謂的全局作用域,在此聲明的變量在函數(shù)內(nèi)部也可以被訪問。
全局作用域中聲明的變量,任何其它作用域都可以被訪問。
注意:文章來源地址http://www.zghlxwxcb.cn/news/detail-709923.html
- 為window對(duì)象動(dòng)態(tài)添加的屬性默認(rèn)也是全局的,不推薦!
- 函數(shù)中未使用任何關(guān)鍵字聲明的變量為全局變量,不推薦?。?/li>
- 盡可能少的聲明全局變量,防止全局變量被污染。
作用域鏈
作用域鏈本質(zhì)上是底層的變量查找機(jī)制。
- 在函數(shù)被執(zhí)行時(shí),會(huì)優(yōu)先查找當(dāng)前函數(shù)作用域中查找變量;
- 如果當(dāng)前作用域查找不到則會(huì)依次逐級(jí)查找父級(jí)作用域直到全局作用域。
總結(jié):
- 嵌套關(guān)系的作用域串聯(lián)起來形成了作用域鏈
- 相同作用域鏈中按著從小到大的規(guī)則查找變量
- 子作用域能夠訪問父作用域,父級(jí)作用域無(wú)法訪問子級(jí)作用域
垃圾回收機(jī)制
內(nèi)存的生命周期
JS環(huán)境中分配的內(nèi)存,一般有如下生命周期:
- 內(nèi)存分配:當(dāng)我們聲明變量、函數(shù)、對(duì)象的時(shí)候,系統(tǒng)會(huì)自動(dòng)為他們分配內(nèi)存
- 內(nèi)存使用:即讀寫內(nèi)存,也就是使用變量、函數(shù)等
- 內(nèi)存回收:使用完畢,由垃圾回收器自動(dòng)回收不再使用的內(nèi)存
// 為變量分配內(nèi)存
const num = 10;
// 為對(duì)象分配內(nèi)存
const obj = {
num: 10
}
// 為函數(shù)分配內(nèi)存
function fn(){
const num = 10;
console.log(num);
}
說明:
- 全局變量一般不會(huì)回收(關(guān)閉頁(yè)面回收)
- 一般情況下局部變量的值,不用了,會(huì)被自動(dòng)回收掉
內(nèi)存泄漏:程序中分配的內(nèi)存由于某種原因程序未釋放或無(wú)法釋放叫做內(nèi)存泄漏。
算法說明
這一部分介紹JS引擎是如何回收內(nèi)存的。
復(fù)習(xí)
堆??臻g分配區(qū)別:
- 棧(操作系統(tǒng)):由操作系統(tǒng)自動(dòng)分配釋放函數(shù)的參數(shù)值、局部變量等,基本數(shù)據(jù)類型放到棧里面。
- 堆(操作系統(tǒng)):一般由程序員分配釋放,若程序員不釋放,由垃圾回收機(jī)制回收。復(fù)雜數(shù)據(jù)類型放到堆里面。
下面介紹兩種常見的瀏覽器垃圾回收算法:引用計(jì)數(shù)法和標(biāo)記清除法。
引用計(jì)數(shù)法
IE采用的引用計(jì)數(shù)算法,定義“內(nèi)存不再使用”,就是看一個(gè)對(duì)象是否有指向它的引用,沒有引用了就回收對(duì)象
算法:
- 跟蹤記錄被引用的次數(shù);
- 如果被引用了一次,那么就記錄次數(shù)1,多次引用會(huì)累加;
- 如果減少一個(gè)引用就減1;
- 如果引用次數(shù)是0,則釋放內(nèi)存。
引用計(jì)數(shù)算法是個(gè)簡(jiǎn)單有效的算法,但是現(xiàn)在已經(jīng)很少使用,因?yàn)樗嬖谝粋€(gè)致命的問題:嵌套引用(循環(huán)引用)
如果兩個(gè)對(duì)象相互引用,盡管他們已不再使用,垃圾回收器不會(huì)進(jìn)行回收,導(dǎo)致內(nèi)存泄漏。
function fn(){
let o1 = {}
let o2 = {}
o1.a = o2
o2.a = o1
return '引用計(jì)數(shù)無(wú)法回收'
}
fn()
如上圖,函數(shù)執(zhí)行結(jié)束后,局部變量都被取消,但是由于對(duì)象相互引用,內(nèi)存無(wú)法被回收。
并且,每執(zhí)行一次函數(shù),就會(huì)導(dǎo)致一次內(nèi)存泄漏。
標(biāo)記清除法
現(xiàn)代的瀏覽器已經(jīng)不再使用引用計(jì)數(shù)算法了。
現(xiàn)代瀏覽器通用的大多是基于標(biāo)記清除算法的某些改進(jìn)算法,總體思想都是一致的。
核心:
- 標(biāo)記清除算法將“不再使用的對(duì)象”定義為“無(wú)法達(dá)到的對(duì)象”。
- 就是從根部(在S中就是全局對(duì)象)出發(fā)定時(shí)掃描內(nèi)存中的對(duì)象。凡是能從根部到達(dá)的對(duì)象,都是還需要使用的。
- 那些無(wú)法由根部出發(fā)觸及到的對(duì)象被標(biāo)記為不再使用,稍后進(jìn)行回收。
如圖,標(biāo)記清除法可以解決引用計(jì)數(shù)法無(wú)法解決的相互引用的問題。
閉包
概念:一個(gè)函數(shù)對(duì)周圍狀態(tài)的引用捆綁在一起,內(nèi)層函數(shù)中訪問到其外層函數(shù)的作用域
簡(jiǎn)單理解:閉包 = 內(nèi)層函數(shù) + 外層函數(shù)的變量
function outer(){
const num = 10;
function fn(){
console.log(num);
}
fn();
}
outer(); // 10
內(nèi)函數(shù)fn
使用了外函數(shù)的變量num
,形成閉包。
另一個(gè)例子:統(tǒng)計(jì)函數(shù)調(diào)用次數(shù),函數(shù)調(diào)用一次,就加1。
function counter(){
let num = 0;
return function(){
console.log(num++);
}
}
const add = counter();
add(); // 0
add(); // 1
add(); // 2
閉包作用:封閉數(shù)據(jù),提供操作,外部也可以訪問函數(shù)內(nèi)部的變量。
閉包應(yīng)用:實(shí)現(xiàn)數(shù)據(jù)的私有。
變量提升
變量提升是JavaScript中比較“奇怪”的現(xiàn)象,它允許在變量聲明之前即被訪問(僅存在于var聲明變量)
console.log(num); // undefined
var num = 10;
在這個(gè)案例中,使用var聲明的num
會(huì)存在變量提升的現(xiàn)象。提前輸出不會(huì)報(bào)錯(cuò),是因?yàn)檫@個(gè)變量已經(jīng)聲明了。雖然聲明會(huì)被提升,但是賦值操作不會(huì)被提升,所以num
還是undefined
。文章來源:http://www.zghlxwxcb.cn/news/detail-709923.html
注意:
- 變量在未聲明即被訪問時(shí)會(huì)報(bào)語(yǔ)法錯(cuò)誤;
- 變量在var聲明之前即被訪問,變量的值為undefined;
- let/const聲明的變量不存在變量提升;
- 變量提升出現(xiàn)在相同作用域當(dāng)中;
- 實(shí)際開發(fā)中推薦先聲明再訪問變量。
到了這里,關(guān)于作用域和作用域鏈的相關(guān)知識(shí)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!