欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Javascript淺析執(zhí)行機制的詳情

 更新時間:2022年08月04日 14:38:35   作者:Celester_best  
這篇文章主要介紹了JavaScript執(zhí)行機制,想要搞懂JavaScript執(zhí)行機制,便與進程與線程的概念脫不了干系,下面我們就來看看這JavaScript執(zhí)行機制的具體介紹吧,需要的朋友可以參考一下

變量提升

要理解變量提升,我們先從一小段代碼入手:

        showName()
        console.log(myname)
        var myname = '測試'
        function showName() {
            console.log('函數(shù)showName被執(zhí)行');
        }

上面的代碼執(zhí)行之后會是什么結(jié)果呢?

按照正常的邏輯分析,當(dāng)執(zhí)行到第 1 行的時候,由于函數(shù) showName 還沒有定義,所以執(zhí)行應(yīng)該會報錯;同樣執(zhí)行第 2 行的時候,由于變量 myname 也未定義,所以同樣也會報錯。但是,我們運行這段代碼的時候,并沒有報錯,而且打印了結(jié)果。

竟然和預(yù)想的不一樣。難道是在Javascript中,變量和方法沒有定義也可以使用嗎?我們注釋掉myname的定義,在運行代碼看結(jié)果會怎樣?

看來在Javascript中,變量或者函數(shù)的使用有需要定義。

我們在看下面的代碼執(zhí)行結(jié)果:

        showName()
        console.log('1: ',myname)
        var myname = '測試'
        console.log('2F: ',myname)
        function showName() {
            console.log('函數(shù)showName被執(zhí)行');
        }

所以,在Javascript中:

1、在執(zhí)行過程中,若使用了未聲明的變量,那么 JavaScript 執(zhí)行會報錯。

2、在一個變量定義之前使用它,不會出錯,但是該變量的值會為 undefined,而不是定義時的值。

3、在一個變量定義之后使用它,該變量的值就是我們定義的值

4、在一個函數(shù)定義之前使用它,不會出錯,且函數(shù)能正確執(zhí)行。

為什么變量和函數(shù)在定義之前使用不會報錯呢?這就設(shè)計到了Javascript中的變量提升。

JavaScript 中的聲明和賦值:

varmyname = '測試'可以拆分為變量的聲明和賦值。

聲明:

var myname

賦值:

myname = '測試'

函數(shù)的聲明和賦值:

聲明:

function(){

console.log('函數(shù)showName被執(zhí)行');

}

賦值:

var showName = function(){

console.log('函數(shù)showName被執(zhí)行');

}

ps:

函數(shù)是先聲明變量 showName ,再把function(){console.log('函數(shù)showName被執(zhí)行');}賦值給showName。

變量提升,是指在 JavaScript 代碼執(zhí)行過程中,JavaScript 引擎把變量的聲明部分和函數(shù)的聲明部分提升到代碼開頭的“行為”。變量被提升后,會給變量設(shè)置默認(rèn)值,這個默認(rèn)值就是undefined。所以這也解釋了為什么第一段代碼中myname打印的值是undefined了。

所以,第一段代碼可以這樣理解:

JavaScript 代碼的執(zhí)行流程

從概念的字面意義上來看,“變量提升”意味著變量和函數(shù)的聲明會在物理層面移動到代碼的最前面,正如我們所模擬的那樣。但,這并不準(zhǔn)確。實際上變量和函數(shù)聲明在代碼里的位置是不會改變的,而且是在編譯階段被 JavaScript 引擎放入內(nèi)存中。

代碼的執(zhí)行分為編譯階段和執(zhí)行階段

編譯階段

根據(jù)上面的理解可知,代碼可以分為變量提升的代碼和可執(zhí)行的代碼。

變量提升部分的代碼:

var myname = undefined
function showName() {
    console.log('函數(shù)showName被執(zhí)行');
}

執(zhí)行部分的代碼:

showName()
console.log(myname)
myname = '極客時間'

代碼經(jīng)過編譯之后會生產(chǎn)兩部分內(nèi)容:執(zhí)行上下文和可執(zhí)行代碼

執(zhí)行上下文是 JavaScript 執(zhí)行一段代碼時的運行環(huán)境,比如調(diào)用一個函數(shù),就會進入這個函數(shù)的執(zhí)行上下文,確定該函數(shù)在執(zhí)行期間用到的諸如 this、變量、對象以及函數(shù)等。

在執(zhí)行上下文中存在一個變量環(huán)境的對象(Viriable Environment),該對象中保存了變量提升的內(nèi)容,比如上面代碼中的變量 myname 和函數(shù) showName,都保存在該對象中。

接下來,我們根據(jù)代碼一行一行的分析下是如何生成變量環(huán)境對象的:

第 1 行和第 2 行,由于這兩行代碼不是聲明操作,所以 JavaScript 引擎不會做任何處理;

第 3 行,由于這行是經(jīng)過 var 聲明的,因此 JavaScript 引擎將在環(huán)境對象中創(chuàng)建一個名為 myname 的屬性,并使用 undefined 對其初始化;

第 4 行,JavaScript 引擎發(fā)現(xiàn)了一個通過 function 定義的函數(shù),所以它將函數(shù)定義存儲到堆 (HEAP)中,并在環(huán)境對象中創(chuàng)建一個 showName 的屬性,然后將該屬性值指向堆中函數(shù)的位置(不了解堆也沒關(guān)系,JavaScript 的執(zhí)行堆和執(zhí)行棧我會在后續(xù)文章中介紹)。這樣就生成了變量環(huán)境對象。接下來 JavaScript 引擎會把聲明以外的代碼編譯為字節(jié)碼。

執(zhí)行階段

我們就來一行一行分析下這個執(zhí)行過程:

(1)當(dāng)執(zhí)行到 showName 函數(shù)時,JavaScript 引擎便開始在變量環(huán)境對象中查找該函數(shù),由于變量環(huán)境對象中存在該函數(shù)的引用,所以 JavaScript 引擎便開始執(zhí)行該函數(shù),并輸出“函數(shù) showName 被執(zhí)行”結(jié)果。

(2)接下來打印“myname”信息,JavaScript 引擎繼續(xù)在變量環(huán)境對象中查找該對象,由于變量環(huán)境存在 myname 變量,并且其值為 undefined,所以這時候就輸出 undefined。

(3)接下來執(zhí)行第 3 行,把“極客時間”賦給 myname 變量,賦值后變量環(huán)境中的 myname 屬性值改變?yōu)?ldquo;極客時間”。

代碼中出現(xiàn)相同的變量或者函數(shù)怎么辦

function showName() {
    console.log('極客邦');
}
showName();
function showName() {
    console.log('極客時間');
}
showName(); 

上面的代碼會輸出什么呢?

我們來分析下其完整執(zhí)行流程:

(1)首先是編譯階段。遇到了第一個 showName 函數(shù),會將該函數(shù)體存放到變量環(huán)境中。接下來是第二個 showName 函數(shù),繼續(xù)存放至變量環(huán)境中,但是變量環(huán)境中已經(jīng)存在一個 showName 函數(shù)了,此時,第二個 showName 函數(shù)會將第一個 showName 函數(shù)覆蓋掉。這樣變量環(huán)境中就只存在第二個 showName 函數(shù)了。

(2)接下來是執(zhí)行階段。先執(zhí)行第一個 showName 函數(shù),但由于是從變量環(huán)境中查找 showName 函數(shù),而變量環(huán)境中只保存了第二個 showName 函數(shù),所以最終調(diào)用的是第二個函數(shù),打印的內(nèi)容是“極客時間”。第二次執(zhí)行 showName 函數(shù)也是走同樣的流程,所以輸出的結(jié)果也是“極客時間”。綜上所述,一段代碼如果定義了兩個相同名字的函數(shù),那么最終生效的是最后一個函數(shù)。

        console.log('1:', myname)
        var myname = 'zhangsan'
        console.log('2:', myname)
        var myname = 'lisi'
        console.log('3:', myname)

上面的代碼的執(zhí)行結(jié)果又是怎樣的呢?

分析:

(1)編譯階段。遇到第一個var myname時,會將該變量存放到變量環(huán)境中,值為undefined。接著遇到第二個var myname時,繼續(xù)存放至變量環(huán)境中,值也為undefined,但是變量環(huán)境中已經(jīng)存在一個 myname變量了,此時,第二個 myname會將第一個 myname覆蓋掉。這樣變量環(huán)境中就只存在第二個 myname變量了,值為undefined。

(2)執(zhí)行階段。執(zhí)行第一個console打印時,myname的值為undefined,所以打印的是undefined。直接是對myname的賦值,值為zhangsan,所以執(zhí)行第二個console的時候,打印的是zhangsan,接著執(zhí)行第二條賦值語句,myname被賦值為lisi,所以在執(zhí)行第三個console時,打印的是lisi。

什么情況下會進行編譯并創(chuàng)建執(zhí)行上下文?

1、當(dāng)Javascript執(zhí)行全家代碼時,會編譯全局代碼并創(chuàng)建全局執(zhí)行上下文,而且整個頁面的生命周期內(nèi),全局執(zhí)行上下文只有一份

2、當(dāng)調(diào)用一個函數(shù)的時候,函數(shù)體內(nèi)的代碼會被編譯,并創(chuàng)建函數(shù)執(zhí)行上下文,一般情況下,函數(shù)執(zhí)行結(jié)束之后,創(chuàng)建的函數(shù)執(zhí)行上下文會被銷毀。

3、當(dāng)使用eval函數(shù)的時候,eval的代碼會被編譯,并創(chuàng)建執(zhí)行上下文。

到此這篇關(guān)于Javascript淺析執(zhí)行機制的詳情的文章就介紹到這了,更多相關(guān)Js執(zhí)行機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論