JavaScript 執(zhí)行
一段 JavaScript 經(jīng)過編譯會生成兩部分:執(zhí)行上下文,可執(zhí)行代碼。
在執(zhí)行上下文中存在一個變量環(huán)境對象(Viriable Environment),保存了變量提升的內(nèi)容。
簡單的變量環(huán)境對象
VariableEnvironment:
myname -> undefined,
showName ->function : {console.log(myname)
JavaScript 代碼先編譯在執(zhí)行
showName()
console.log(myname)
var myname = 'hello'
function showName() {
console.log('函數(shù)showName被執(zhí)行');
}
- JS 引擎在環(huán)境對象中創(chuàng)建一個名為myname屬性,并用
undefined對其初始; - JS 引擎發(fā)現(xiàn)同 function 定義的函數(shù),把函數(shù)定義存儲在堆(HEAP)中,并在環(huán)境對象中創(chuàng)建一個 showName 的屬性,然后將地址指向堆中的環(huán)境位置;
- JS 引擎會把聲明外的代碼編譯為字節(jié)碼
調(diào)用棧 call statck
調(diào)用棧是用來管理函數(shù)調(diào)用關(guān)系的一種數(shù)據(jù)結(jié)構(gòu)
把這種用來管理執(zhí)行上下文的棧成為執(zhí)行上下文棧,又稱調(diào)用棧
console.track() 查看調(diào)用棧
let 和 const
作用域 scope
作用域是指在程序中定義變量的區(qū)域,該位置決定了變量的生命周期。通俗地理解,作用域就是變量與函數(shù)的可訪問范圍,即作用域控制著變量和函數(shù)的可見性和生命周期。
- 全局作用域中的對象在代碼中的任何地方都能訪問,其生命周期伴隨著頁面的生命周期
- 函數(shù)作用域就是在函數(shù)內(nèi)部定義的變量或者函數(shù),并且定義的變量或者函數(shù)只能在函數(shù)內(nèi)部被訪問。函數(shù)執(zhí)行結(jié)束之后,函數(shù)內(nèi)部定義的變量會被銷毀
代碼塊內(nèi)部定義的變量外部是訪問不到的,代碼塊中的代碼執(zhí)行完成,代碼塊中定義的變量就會銷毀。
S6 之前是不支持塊級作用域的,因?yàn)楫?dāng)初設(shè)計(jì)這門語言的時候,并沒有想到 JavaScript 會火起來,所以只是按照最簡單的方式來設(shè)計(jì)。沒有了塊級作用域,再把作用域內(nèi)部的變量統(tǒng)一提升無疑是最快速、最簡單的設(shè)計(jì),不過這也直接導(dǎo)致了函數(shù)中的變量無論是在哪里聲明的,在編譯階段都會被提取到執(zhí)行上下文的變量環(huán)境中,所以這些變量在整個函數(shù)體內(nèi)部的任何地方都是能被訪問的,這也就是 JavaScript 中的變量提升。
ES6 是如何做到既要支持變量提升的特性,又要支持塊級作用域的呢?”
- 第一步是編譯并創(chuàng)建執(zhí)行上下文,下面是我畫出來的執(zhí)行上下文示意圖,你可以參考下:
- 函數(shù)內(nèi)部通過 var 聲明的變量,在編譯階段全都被存放到變量環(huán)境里面了。
- 通過 let 聲明的變量,在編譯階段會被存放到詞法環(huán)境(Lexical Environment)中
- 在函數(shù)的作用域內(nèi)部,通過 let 聲明的變量并沒有被存放到詞法環(huán)境中。
作用域鏈
每個執(zhí)行上下文的變量環(huán)境中,都包含一個外部引用,
用來指向外部的執(zhí)行上下文,我們把這個外部引用成為outer.
詞法作用域
詞法作用域是指作用域是有代碼中函數(shù)聲明的
法作用域是代碼階段就決定好的,和函數(shù)是怎么調(diào)用的沒有關(guān)系
詞法作用域
詞法作用域是指作用域是由代碼中函數(shù)聲明的位置來決定的,所以詞法作用域是靜態(tài)作用域。詞法作用域是代碼階段就確定好的,和函數(shù)執(zhí)行無關(guān)。
閉包
function foo() {
var myName = "li"
let test1 = 1
const test2 = 2
var innerBar = {
getName:function(){
console.log(test1)
return myName
},
setName:function(newName){
myName = newName
}
}
return innerBar
}
var bar = foo()
bar.setName("liyunfei")
bar.getName()
console.log(bar.getName())
當(dāng)執(zhí)行到foo函數(shù)時的調(diào)用棧情況
foo函數(shù)執(zhí)行上下文 =》變量環(huán)境, 詞法環(huán)境
全局執(zhí)行上下文 =》變量環(huán)境, 詞法環(huán)境
閉包是怎么回收的
this
執(zhí)行上下文:
變量環(huán)境、詞法環(huán)境、outer、this;